TUST
Script Library and Template Collection for Gamestudio A8
 All Classes Namespaces Files Functions Variables Typedefs Macros
CImg.h
1 /*
2  #
3  # File : CImg.h
4  # ( C++ header file )
5  #
6  # Description : The C++ Template Image Processing Toolkit.
7  # This file is the main component of the CImg Library project.
8  # ( http://cimg.sourceforge.net )
9  #
10  # Project manager : David Tschumperle.
11  # ( http://tschumperle.users.greyc.fr/ )
12  #
13  # A complete list of contributors is available in file 'README.txt'
14  # distributed within the CImg package.
15  #
16  # Licenses : This file is 'dual-licensed', you have to choose one
17  # of the two licenses below to apply.
18  #
19  # CeCILL-C
20  # The CeCILL-C license is close to the GNU LGPL.
21  # ( http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html )
22  #
23  # or CeCILL v2.0
24  # The CeCILL license is compatible with the GNU GPL.
25  # ( http://www.cecill.info/licences/Licence_CeCILL_V2-en.html )
26  #
27  # This software is governed either by the CeCILL or the CeCILL-C license
28  # under French law and abiding by the rules of distribution of free software.
29  # You can use, modify and or redistribute the software under the terms of
30  # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA
31  # at the following URL: "http://www.cecill.info".
32  #
33  # As a counterpart to the access to the source code and rights to copy,
34  # modify and redistribute granted by the license, users are provided only
35  # with a limited warranty and the software's author, the holder of the
36  # economic rights, and the successive licensors have only limited
37  # liability.
38  #
39  # In this respect, the user's attention is drawn to the risks associated
40  # with loading, using, modifying and/or developing or reproducing the
41  # software by the user in light of its specific status of free software,
42  # that may mean that it is complicated to manipulate, and that also
43  # therefore means that it is reserved for developers and experienced
44  # professionals having in-depth computer knowledge. Users are therefore
45  # encouraged to load and test the software's suitability as regards their
46  # requirements in conditions enabling the security of their systems and/or
47  # data to be ensured and, more generally, to use and operate it in the
48  # same conditions as regards security.
49  #
50  # The fact that you are presently reading this means that you have had
51  # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms.
52  #
53 */
54 
55 // Set version number of the library.
56 #ifndef cimg_version
57 #define cimg_version 152
58 
59 /*-----------------------------------------------------------
60  #
61  # Test and possibly auto-set CImg configuration variables
62  # and include required headers.
63  #
64  # If you find that the default configuration variables are
65  # not adapted to your system, you can override their values
66  # before including the header file "CImg.h"
67  # (use the #define directive).
68  #
69  ------------------------------------------------------------*/
70 
71 // Include standard C++ headers.
72 // This is the minimal set of required headers to make CImg-based codes compile.
73 #include <cstdio>
74 #include <cstdlib>
75 #include <cstdarg>
76 #include <cstring>
77 #include <cmath>
78 #include <ctime>
79 #include <exception>
80 
81 // Detect/configure OS variables.
82 //
83 // Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies).
84 // '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
85 // '2' for Microsoft Windows.
86 // (auto-detection is performed if 'cimg_OS' is not set by the user).
87 #ifndef cimg_OS
88 #if defined(unix) || defined(__unix) || defined(__unix__) \
89  || defined(linux) || defined(__linux) || defined(__linux__) \
90  || defined(sun) || defined(__sun) \
91  || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \
92  || defined(__FreeBSD__) || defined __DragonFly__ \
93  || defined(sgi) || defined(__sgi) \
94  || defined(__MACOSX__) || defined(__APPLE__) \
95  || defined(__CYGWIN__)
96 #define cimg_OS 1
97 #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \
98  || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
99 #define cimg_OS 2
100 #else
101 #define cimg_OS 0
102 #endif
103 #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
104 #error CImg Library: Invalid configuration variable 'cimg_OS'.
105 #error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
106 #endif
107 
108 // Disable silly warnings on some Microsoft VC++ compilers.
109 #ifdef _MSC_VER
110 #pragma warning(push)
111 #pragma warning(disable:4311)
112 #pragma warning(disable:4312)
113 #pragma warning(disable:4800)
114 #pragma warning(disable:4804)
115 #pragma warning(disable:4996)
116 #define _CRT_SECURE_NO_DEPRECATE 1
117 #define _CRT_NONSTDC_NO_DEPRECATE 1
118 #endif
119 
120 // Include OS-specific headers.
121 #if cimg_OS==1
122 #include <sys/types.h>
123 #include <sys/time.h>
124 #include <unistd.h>
125 #elif cimg_OS==2
126 #ifndef NOMINMAX
127 #define NOMINMAX
128 #endif
129 #include <windows.h>
130 #ifndef _WIN32_IE
131 #define _WIN32_IE 0x0400
132 #endif
133 #include <shlobj.h>
134 #include <process.h>
135 #include <io.h>
136 #define cimg_snprintf _snprintf
137 #define cimg_vsnprintf _vsnprintf
138 #endif
139 #ifndef cimg_snprintf
140 #include <stdio.h>
141 #define cimg_snprintf snprintf
142 #define cimg_vsnprintf vsnprintf
143 #endif
144 
145 // Configure filename separator.
146 //
147 // Filename separator is set by default to '/', except for Windows where it is '\'.
148 #ifndef cimg_file_separator
149 #if cimg_OS==2
150 #define cimg_file_separator '\\'
151 #else
152 #define cimg_file_separator '/'
153 #endif
154 #endif
155 
156 // Configure verbosity of output messages.
157 //
158 // Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode).
159 // '1' to output library messages on the console.
160 // '2' to output library messages on a basic dialog window (default behavior).
161 // '3' to do as '1' + add extra warnings (may slow down the code!).
162 // '4' to do as '2' + add extra warnings (may slow down the code!).
163 //
164 // Define 'cimg_strict_warnings' to replace warning messages by exception throwns.
165 //
166 // Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals.
167 #ifndef cimg_verbosity
168 #define cimg_verbosity 2
169 #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4)
170 #error CImg Library: Configuration variable 'cimg_verbosity' is badly defined.
171 #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }).
172 #endif
173 
174 // Configure display framework.
175 //
176 // Define 'cimg_display' to: '0' to disable display capabilities.
177 // '1' to use the X-Window framework (X11).
178 // '2' to use the Microsoft GDI32 framework.
179 #ifndef cimg_display
180 #if cimg_OS==0
181 #define cimg_display 0
182 #elif cimg_OS==1
183 #if defined(__MACOSX__) || defined(__APPLE__)
184 #define cimg_display 1
185 #else
186 #define cimg_display 1
187 #endif
188 #elif cimg_OS==2
189 #define cimg_display 2
190 #endif
191 #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
192 #error CImg Library: Configuration variable 'cimg_display' is badly defined.
193 #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
194 #endif
195 
196 // Include display-specific headers.
197 #if cimg_display==1
198 #include <X11/Xlib.h>
199 #include <X11/Xutil.h>
200 #include <X11/keysym.h>
201 #include <pthread.h>
202 #ifdef cimg_use_xshm
203 #include <sys/ipc.h>
204 #include <sys/shm.h>
205 #include <X11/extensions/XShm.h>
206 #endif
207 #ifdef cimg_use_xrandr
208 #include <X11/extensions/Xrandr.h>
209 #endif
210 #endif
211 #ifndef cimg_appname
212 #define cimg_appname "CImg"
213 #endif
214 
215 // Configure OpenMP support.
216 // (http://www.openmp.org)
217 //
218 // Define 'cimg_use_openmp' to enable OpenMP support.
219 //
220 // OpenMP directives may be used in a (very) few CImg functions to get
221 // advantages of multi-core CPUs.
222 #ifdef cimg_use_openmp
223 #include "omp.h"
224 #define _cimg_static
225 #else
226 #define _cimg_static static
227 #endif
228 
229 // Configure OpenCV support.
230 // (http://opencv.willowgarage.com/wiki/)
231 //
232 // Define 'cimg_use_opencv' to enable OpenCV support.
233 //
234 // OpenCV library may be used to access images from cameras
235 // (see method 'CImg<T>::load_camera()').
236 #ifdef cimg_use_opencv
237 #ifdef True
238 #undef True
239 #define _cimg_redefine_True
240 #endif
241 #ifdef False
242 #undef False
243 #define _cimg_redefine_False
244 #endif
245 #include <cstddef>
246 #include "cv.h"
247 #include "highgui.h"
248 #endif
249 
250 // Configure LibPNG support.
251 // (http://www.libpng.org)
252 //
253 // Define 'cimg_use_png' to enable LibPNG support.
254 //
255 // PNG library may be used to get a native support of '.png' files.
256 // (see methods 'CImg<T>::{load,save}_png()'.
257 #ifdef cimg_use_png
258 extern "C" {
259 #include "png.h"
260 }
261 #endif
262 
263 // Configure LibJPEG support.
264 // (http://en.wikipedia.org/wiki/Libjpeg)
265 //
266 // Define 'cimg_use_jpeg' to enable LibJPEG support.
267 //
268 // JPEG library may be used to get a native support of '.jpg' files.
269 // (see methods 'CImg<T>::{load,save}_jpeg()').
270 #ifdef cimg_use_jpeg
271 extern "C" {
272 #include "jpeglib.h"
273 #include "setjmp.h"
274 }
275 #endif
276 
277 // Configure LibTIFF support.
278 // (http://www.libtiff.org)
279 //
280 // Define 'cimg_use_tiff' to enable LibTIFF support.
281 //
282 // TIFF library may be used to get a native support of '.tif' files.
283 // (see methods 'CImg[List]<T>::{load,save}_tiff()').
284 #ifdef cimg_use_tiff
285 extern "C" {
286 #include "tiffio.h"
287 }
288 #endif
289 
290 // Configure LibMINC2 support.
291 // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference)
292 //
293 // Define 'cimg_use_minc2' to enable LibMINC2 support.
294 //
295 // MINC2 library may be used to get a native support of '.mnc' files.
296 // (see methods 'CImg<T>::{load,save}_minc2()').
297 #ifdef cimg_use_minc2
298 #include "minc_io_simple_volume.h"
299 #include "minc_1_simple.h"
300 #include "minc_1_simple_rw.h"
301 #endif
302 
303 // Configure FFMPEG support.
304 // (http://www.ffmpeg.org)
305 //
306 // Define 'cimg_use_ffmpeg' to enable FFMPEG lib support.
307 //
308 // Avcodec and Avformat libraries from FFMPEG may be used
309 // to get a native support of various video file formats.
310 // (see methods 'CImg[List]<T>::load_ffmpeg()').
311 #ifdef cimg_use_ffmpeg
312 #if (defined(_STDINT_H) || defined(_STDINT_H_)) && !defined(UINT64_C)
313 #warning "__STDC_CONSTANT_MACROS has to be defined before including <stdint.h>, this file will probably not compile."
314 #endif
315 #ifndef __STDC_CONSTANT_MACROS
316 #define __STDC_CONSTANT_MACROS // ...or stdint.h wont' define UINT64_C, needed by libavutil
317 #endif
318 extern "C" {
319 #include "avformat.h"
320 #include "avcodec.h"
321 #include "swscale.h"
322 }
323 #endif
324 
325 // Configure Zlib support.
326 // (http://www.zlib.net)
327 //
328 // Define 'cimg_use_zlib' to enable Zlib support.
329 //
330 // Zlib library may be used to allow compressed data in '.cimgz' files
331 // (see methods 'CImg[List]<T>::{load,save}_cimg()').
332 #ifdef cimg_use_zlib
333 extern "C" {
334 #include "zlib.h"
335 }
336 #endif
337 
338 // Configure Magick++ support.
339 // (http://www.imagemagick.org/Magick++)
340 //
341 // Define 'cimg_use_magick' to enable Magick++ support.
342 //
343 // Magick++ library may be used to get a native support of various image file formats.
344 // (see methods 'CImg<T>::{load,save}()').
345 #ifdef cimg_use_magick
346 #include "Magick++.h"
347 #endif
348 
349 // Configure FFTW3 support.
350 // (http://www.fftw.org)
351 //
352 // Define 'cimg_use_fftw3' to enable libFFTW3 support.
353 //
354 // FFTW3 library may be used to efficiently compute the Fast Fourier Transform
355 // of image data, without restriction on the image size.
356 // (see method 'CImg[List]<T>::FFT()').
357 #ifdef cimg_use_fftw3
358 extern "C" {
359 #include "fftw3.h"
360 }
361 #endif
362 
363 // Configure LibBoard support.
364 // (http://libboard.sourceforge.net/)
365 //
366 // Define 'cimg_use_board' to enable Board support.
367 //
368 // Board library may be used to draw 3d objects in vector-graphics canvas
369 // that can be saved as '.ps' or '.svg' files afterwards.
370 // (see method 'CImg<T>::draw_object3d()').
371 #ifdef cimg_use_board
372 #ifdef None
373 #undef None
374 #define _cimg_redefine_None
375 #endif
376 #include "Board.h"
377 #endif
378 
379 // Configure OpenEXR support.
380 // (http://www.openexr.com/)
381 //
382 // Define 'cimg_use_openexr' to enable OpenEXR support.
383 //
384 // OpenEXR library may be used to get a native support of '.exr' files.
385 // (see methods 'CImg<T>::{load,save}_exr()').
386 #ifdef cimg_use_openexr
387 #include "ImfRgbaFile.h"
388 #include "ImfInputFile.h"
389 #include "ImfChannelList.h"
390 #include "ImfMatrixAttribute.h"
391 #include "ImfArray.h"
392 #endif
393 
394 // Lapack configuration.
395 // (http://www.netlib.org/lapack)
396 //
397 // Define 'cimg_use_lapack' to enable LAPACK support.
398 //
399 // Lapack library may be used in several CImg methods to speed up
400 // matrix computations (eigenvalues, inverse, ...).
401 #ifdef cimg_use_lapack
402 extern "C" {
403  extern void sgetrf_(int*, int*, float*, int*, int*, int*);
404  extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
405  extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
406  extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
407  extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
408  extern void dgetrf_(int*, int*, double*, int*, int*, int*);
409  extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
410  extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
411  extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*, int*, double*, int*, double*, int*, int*);
412  extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
413  extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*);
414  extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*);
415 }
416 #endif
417 
418 // Check if min/max/PI macros are defined.
419 //
420 // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
421 // because it redefines functions min(), max() and const variable PI in the cimg:: namespace.
422 // so it '#undef' these macros if necessary, and restore them to reasonable
423 // values at the end of this file.
424 #ifdef min
425 #undef min
426 #define _cimg_redefine_min
427 #endif
428 #ifdef max
429 #undef max
430 #define _cimg_redefine_max
431 #endif
432 #ifdef PI
433 #undef PI
434 #define _cimg_redefine_PI
435 #endif
436 
437 // Define 'cimg_library' namespace suffix.
438 //
439 // You may want to add a suffix to the 'cimg_library' namespace, for instance if you need to work
440 // with several versions of the library at the same time.
441 #ifdef cimg_namespace_suffix
442 #define __cimg_library_suffixed(s) cimg_library_##s
443 #define _cimg_library_suffixed(s) __cimg_library_suffixed(s)
444 #define cimg_library_suffixed _cimg_library_suffixed(cimg_namespace_suffix)
445 #else
446 #define cimg_library_suffixed cimg_library
447 #endif
448 
449 /*------------------------------------------------------------------------------
450  #
451  # Define user-friendly macros.
452  #
453  # These CImg macros are prefixed by 'cimg_' and can be used safely in your own
454  # code. They are useful to parse command line options, or to write image loops.
455  #
456  ------------------------------------------------------------------------------*/
457 
458 // Macros to define program usage, and retrieve command line arguments.
459 #define cimg_usage(usage) cimg_library_suffixed::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
460 #define cimg_help(str) cimg_library_suffixed::cimg::option((char*)0,argc,argv,str,(char*)0)
461 #define cimg_option(name,defaut,usage) cimg_library_suffixed::cimg::option(name,argc,argv,defaut,usage)
462 #define cimg_argument(pos) cimg_library_suffixed::cimg::argument(pos,argc,argv)
463 #define cimg_argument1(pos,s0) cimg_library_suffixed::cimg::argument(pos,argc,argv,1,s0)
464 #define cimg_argument2(pos,s0,s1) cimg_library_suffixed::cimg::argument(pos,argc,argv,2,s0,s1)
465 #define cimg_argument3(pos,s0,s1,s2) cimg_library_suffixed::cimg::argument(pos,argc,argv,3,s0,s1,s2)
466 #define cimg_argument4(pos,s0,s1,s2,s3) cimg_library_suffixed::cimg::argument(pos,argc,argv,4,s0,s1,s2,s3)
467 #define cimg_argument5(pos,s0,s1,s2,s3,s4) cimg_library_suffixed::cimg::argument(pos,argc,argv,5,s0,s1,s2,s3,s4)
468 #define cimg_argument6(pos,s0,s1,s2,s3,s4,s5) cimg_library_suffixed::cimg::argument(pos,argc,argv,6,s0,s1,s2,s3,s4,s5)
469 #define cimg_argument7(pos,s0,s1,s2,s3,s4,s5,s6) cimg_library_suffixed::cimg::argument(pos,argc,argv,7,s0,s1,s2,s3,s4,s5,s6)
470 #define cimg_argument8(pos,s0,s1,s2,s3,s4,s5,s6,s7) cimg_library_suffixed::cimg::argument(pos,argc,argv,8,s0,s1,s2,s3,s4,s5,s6,s7)
471 #define cimg_argument9(pos,s0,s1,s2,s3,s4,s5,s6,s7,s8) cimg_library_suffixed::cimg::argument(pos,argc,argv,9,s0,s1,s2,s3,s4,s5,s6,s7,s8)
472 
473 // Macros to define and manipulate local neighborhoods.
474 #define CImg_2x2(I,T) T I[4]; \
475  T& I##cc = I[0]; T& I##nc = I[1]; \
476  T& I##cn = I[2]; T& I##nn = I[3]; \
477  I##cc = I##nc = \
478  I##cn = I##nn = 0
479 
480 #define CImg_3x3(I,T) T I[9]; \
481  T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
482  T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
483  T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
484  I##pp = I##cp = I##np = \
485  I##pc = I##cc = I##nc = \
486  I##pn = I##cn = I##nn = 0
487 
488 #define CImg_4x4(I,T) T I[16]; \
489  T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
490  T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
491  T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
492  T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
493  I##pp = I##cp = I##np = I##ap = \
494  I##pc = I##cc = I##nc = I##ac = \
495  I##pn = I##cn = I##nn = I##an = \
496  I##pa = I##ca = I##na = I##aa = 0
497 
498 #define CImg_5x5(I,T) T I[25]; \
499  T& I##bb = I[0]; T& I##pb = I[1]; T& I##cb = I[2]; T& I##nb = I[3]; T& I##ab = I[4]; \
500  T& I##bp = I[5]; T& I##pp = I[6]; T& I##cp = I[7]; T& I##np = I[8]; T& I##ap = I[9]; \
501  T& I##bc = I[10]; T& I##pc = I[11]; T& I##cc = I[12]; T& I##nc = I[13]; T& I##ac = I[14]; \
502  T& I##bn = I[15]; T& I##pn = I[16]; T& I##cn = I[17]; T& I##nn = I[18]; T& I##an = I[19]; \
503  T& I##ba = I[20]; T& I##pa = I[21]; T& I##ca = I[22]; T& I##na = I[23]; T& I##aa = I[24]; \
504  I##bb = I##pb = I##cb = I##nb = I##ab = \
505  I##bp = I##pp = I##cp = I##np = I##ap = \
506  I##bc = I##pc = I##cc = I##nc = I##ac = \
507  I##bn = I##pn = I##cn = I##nn = I##an = \
508  I##ba = I##pa = I##ca = I##na = I##aa = 0
509 
510 #define CImg_2x2x2(I,T) T I[8]; \
511  T& I##ccc = I[0]; T& I##ncc = I[1]; \
512  T& I##cnc = I[2]; T& I##nnc = I[3]; \
513  T& I##ccn = I[4]; T& I##ncn = I[5]; \
514  T& I##cnn = I[6]; T& I##nnn = I[7]; \
515  I##ccc = I##ncc = \
516  I##cnc = I##nnc = \
517  I##ccn = I##ncn = \
518  I##cnn = I##nnn = 0
519 
520 #define CImg_3x3x3(I,T) T I[27]; \
521  T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
522  T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
523  T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
524  T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
525  T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
526  T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
527  T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
528  T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
529  T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
530  I##ppp = I##cpp = I##npp = \
531  I##pcp = I##ccp = I##ncp = \
532  I##pnp = I##cnp = I##nnp = \
533  I##ppc = I##cpc = I##npc = \
534  I##pcc = I##ccc = I##ncc = \
535  I##pnc = I##cnc = I##nnc = \
536  I##ppn = I##cpn = I##npn = \
537  I##pcn = I##ccn = I##ncn = \
538  I##pnn = I##cnn = I##nnn = 0
539 
540 #define cimg_get2x2(img,x,y,z,c,I,T) \
541  I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), I[3] = (T)(img)(_n1##x,_n1##y,z,c)
542 
543 #define cimg_get3x3(img,x,y,z,c,I,T) \
544  I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), I[3] = (T)(img)(_p1##x,y,z,c), \
545  I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), \
546  I[8] = (T)(img)(_n1##x,_n1##y,z,c)
547 
548 #define cimg_get4x4(img,x,y,z,c,I,T) \
549  I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), I[3] = (T)(img)(_n2##x,_p1##y,z,c), \
550  I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), \
551  I[8] = (T)(img)(_p1##x,_n1##y,z,c), I[9] = (T)(img)(x,_n1##y,z,c), I[10] = (T)(img)(_n1##x,_n1##y,z,c), I[11] = (T)(img)(_n2##x,_n1##y,z,c), \
552  I[12] = (T)(img)(_p1##x,_n2##y,z,c), I[13] = (T)(img)(x,_n2##y,z,c), I[14] = (T)(img)(_n1##x,_n2##y,z,c), I[15] = (T)(img)(_n2##x,_n2##y,z,c)
553 
554 #define cimg_get5x5(img,x,y,z,c,I,T) \
555  I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), I[3] = (T)(img)(_n1##x,_p2##y,z,c), \
556  I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), \
557  I[8] = (T)(img)(_n1##x,_p1##y,z,c), I[9] = (T)(img)(_n2##x,_p1##y,z,c), I[10] = (T)(img)(_p2##x,y,z,c), I[11] = (T)(img)(_p1##x,y,z,c), \
558  I[12] = (T)(img)(x,y,z,c), I[13] = (T)(img)(_n1##x,y,z,c), I[14] = (T)(img)(_n2##x,y,z,c), I[15] = (T)(img)(_p2##x,_n1##y,z,c), \
559  I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), \
560  I[20] = (T)(img)(_p2##x,_n2##y,z,c), I[21] = (T)(img)(_p1##x,_n2##y,z,c), I[22] = (T)(img)(x,_n2##y,z,c), I[23] = (T)(img)(_n1##x,_n2##y,z,c), \
561  I[24] = (T)(img)(_n2##x,_n2##y,z,c)
562 
563 #define cimg_get6x6(img,x,y,z,c,I,T) \
564  I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), I[3] = (T)(img)(_n1##x,_p2##y,z,c), \
565  I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), \
566  I[8] = (T)(img)(x,_p1##y,z,c), I[9] = (T)(img)(_n1##x,_p1##y,z,c), I[10] = (T)(img)(_n2##x,_p1##y,z,c), I[11] = (T)(img)(_n3##x,_p1##y,z,c), \
567  I[12] = (T)(img)(_p2##x,y,z,c), I[13] = (T)(img)(_p1##x,y,z,c), I[14] = (T)(img)(x,y,z,c), I[15] = (T)(img)(_n1##x,y,z,c), \
568  I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), \
569  I[20] = (T)(img)(x,_n1##y,z,c), I[21] = (T)(img)(_n1##x,_n1##y,z,c), I[22] = (T)(img)(_n2##x,_n1##y,z,c), I[23] = (T)(img)(_n3##x,_n1##y,z,c), \
570  I[24] = (T)(img)(_p2##x,_n2##y,z,c), I[25] = (T)(img)(_p1##x,_n2##y,z,c), I[26] = (T)(img)(x,_n2##y,z,c), I[27] = (T)(img)(_n1##x,_n2##y,z,c), \
571  I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), \
572  I[32] = (T)(img)(x,_n3##y,z,c), I[33] = (T)(img)(_n1##x,_n3##y,z,c), I[34] = (T)(img)(_n2##x,_n3##y,z,c), I[35] = (T)(img)(_n3##x,_n3##y,z,c)
573 
574 #define cimg_get7x7(img,x,y,z,c,I,T) \
575  I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), I[3] = (T)(img)(x,_p3##y,z,c), \
576  I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), \
577  I[8] = (T)(img)(_p2##x,_p2##y,z,c), I[9] = (T)(img)(_p1##x,_p2##y,z,c), I[10] = (T)(img)(x,_p2##y,z,c), I[11] = (T)(img)(_n1##x,_p2##y,z,c), \
578  I[12] = (T)(img)(_n2##x,_p2##y,z,c), I[13] = (T)(img)(_n3##x,_p2##y,z,c), I[14] = (T)(img)(_p3##x,_p1##y,z,c), I[15] = (T)(img)(_p2##x,_p1##y,z,c), \
579  I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), \
580  I[20] = (T)(img)(_n3##x,_p1##y,z,c), I[21] = (T)(img)(_p3##x,y,z,c), I[22] = (T)(img)(_p2##x,y,z,c), I[23] = (T)(img)(_p1##x,y,z,c), \
581  I[24] = (T)(img)(x,y,z,c), I[25] = (T)(img)(_n1##x,y,z,c), I[26] = (T)(img)(_n2##x,y,z,c), I[27] = (T)(img)(_n3##x,y,z,c), \
582  I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), \
583  I[32] = (T)(img)(_n1##x,_n1##y,z,c), I[33] = (T)(img)(_n2##x,_n1##y,z,c), I[34] = (T)(img)(_n3##x,_n1##y,z,c), I[35] = (T)(img)(_p3##x,_n2##y,z,c), \
584  I[36] = (T)(img)(_p2##x,_n2##y,z,c), I[37] = (T)(img)(_p1##x,_n2##y,z,c), I[38] = (T)(img)(x,_n2##y,z,c), I[39] = (T)(img)(_n1##x,_n2##y,z,c), \
585  I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), \
586  I[44] = (T)(img)(_p1##x,_n3##y,z,c), I[45] = (T)(img)(x,_n3##y,z,c), I[46] = (T)(img)(_n1##x,_n3##y,z,c), I[47] = (T)(img)(_n2##x,_n3##y,z,c), \
587  I[48] = (T)(img)(_n3##x,_n3##y,z,c)
588 
589 #define cimg_get8x8(img,x,y,z,c,I,T) \
590  I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), I[3] = (T)(img)(x,_p3##y,z,c), \
591  I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), \
592  I[8] = (T)(img)(_p3##x,_p2##y,z,c), I[9] = (T)(img)(_p2##x,_p2##y,z,c), I[10] = (T)(img)(_p1##x,_p2##y,z,c), I[11] = (T)(img)(x,_p2##y,z,c), \
593  I[12] = (T)(img)(_n1##x,_p2##y,z,c), I[13] = (T)(img)(_n2##x,_p2##y,z,c), I[14] = (T)(img)(_n3##x,_p2##y,z,c), I[15] = (T)(img)(_n4##x,_p2##y,z,c), \
594  I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), \
595  I[20] = (T)(img)(_n1##x,_p1##y,z,c), I[21] = (T)(img)(_n2##x,_p1##y,z,c), I[22] = (T)(img)(_n3##x,_p1##y,z,c), I[23] = (T)(img)(_n4##x,_p1##y,z,c), \
596  I[24] = (T)(img)(_p3##x,y,z,c), I[25] = (T)(img)(_p2##x,y,z,c), I[26] = (T)(img)(_p1##x,y,z,c), I[27] = (T)(img)(x,y,z,c), \
597  I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), \
598  I[32] = (T)(img)(_p3##x,_n1##y,z,c), I[33] = (T)(img)(_p2##x,_n1##y,z,c), I[34] = (T)(img)(_p1##x,_n1##y,z,c), I[35] = (T)(img)(x,_n1##y,z,c), \
599  I[36] = (T)(img)(_n1##x,_n1##y,z,c), I[37] = (T)(img)(_n2##x,_n1##y,z,c), I[38] = (T)(img)(_n3##x,_n1##y,z,c), I[39] = (T)(img)(_n4##x,_n1##y,z,c), \
600  I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), \
601  I[44] = (T)(img)(_n1##x,_n2##y,z,c), I[45] = (T)(img)(_n2##x,_n2##y,z,c), I[46] = (T)(img)(_n3##x,_n2##y,z,c), I[47] = (T)(img)(_n4##x,_n2##y,z,c), \
602  I[48] = (T)(img)(_p3##x,_n3##y,z,c), I[49] = (T)(img)(_p2##x,_n3##y,z,c), I[50] = (T)(img)(_p1##x,_n3##y,z,c), I[51] = (T)(img)(x,_n3##y,z,c), \
603  I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), \
604  I[56] = (T)(img)(_p3##x,_n4##y,z,c), I[57] = (T)(img)(_p2##x,_n4##y,z,c), I[58] = (T)(img)(_p1##x,_n4##y,z,c), I[59] = (T)(img)(x,_n4##y,z,c), \
605  I[60] = (T)(img)(_n1##x,_n4##y,z,c), I[61] = (T)(img)(_n2##x,_n4##y,z,c), I[62] = (T)(img)(_n3##x,_n4##y,z,c), I[63] = (T)(img)(_n4##x,_n4##y,z,c);
606 
607 #define cimg_get9x9(img,x,y,z,c,I,T) \
608  I[0] = (T)(img)(_p4##x,_p4##y,z,c), I[1] = (T)(img)(_p3##x,_p4##y,z,c), I[2] = (T)(img)(_p2##x,_p4##y,z,c), I[3] = (T)(img)(_p1##x,_p4##y,z,c), \
609  I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), \
610  I[8] = (T)(img)(_n4##x,_p4##y,z,c), I[9] = (T)(img)(_p4##x,_p3##y,z,c), I[10] = (T)(img)(_p3##x,_p3##y,z,c), I[11] = (T)(img)(_p2##x,_p3##y,z,c), \
611  I[12] = (T)(img)(_p1##x,_p3##y,z,c), I[13] = (T)(img)(x,_p3##y,z,c), I[14] = (T)(img)(_n1##x,_p3##y,z,c), I[15] = (T)(img)(_n2##x,_p3##y,z,c), \
612  I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), \
613  I[20] = (T)(img)(_p2##x,_p2##y,z,c), I[21] = (T)(img)(_p1##x,_p2##y,z,c), I[22] = (T)(img)(x,_p2##y,z,c), I[23] = (T)(img)(_n1##x,_p2##y,z,c), \
614  I[24] = (T)(img)(_n2##x,_p2##y,z,c), I[25] = (T)(img)(_n3##x,_p2##y,z,c), I[26] = (T)(img)(_n4##x,_p2##y,z,c), I[27] = (T)(img)(_p4##x,_p1##y,z,c), \
615  I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), \
616  I[32] = (T)(img)(_n1##x,_p1##y,z,c), I[33] = (T)(img)(_n2##x,_p1##y,z,c), I[34] = (T)(img)(_n3##x,_p1##y,z,c), I[35] = (T)(img)(_n4##x,_p1##y,z,c), \
617  I[36] = (T)(img)(_p4##x,y,z,c), I[37] = (T)(img)(_p3##x,y,z,c), I[38] = (T)(img)(_p2##x,y,z,c), I[39] = (T)(img)(_p1##x,y,z,c), \
618  I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), \
619  I[44] = (T)(img)(_n4##x,y,z,c), I[45] = (T)(img)(_p4##x,_n1##y,z,c), I[46] = (T)(img)(_p3##x,_n1##y,z,c), I[47] = (T)(img)(_p2##x,_n1##y,z,c), \
620  I[48] = (T)(img)(_p1##x,_n1##y,z,c), I[49] = (T)(img)(x,_n1##y,z,c), I[50] = (T)(img)(_n1##x,_n1##y,z,c), I[51] = (T)(img)(_n2##x,_n1##y,z,c), \
621  I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), \
622  I[56] = (T)(img)(_p2##x,_n2##y,z,c), I[57] = (T)(img)(_p1##x,_n2##y,z,c), I[58] = (T)(img)(x,_n2##y,z,c), I[59] = (T)(img)(_n1##x,_n2##y,z,c), \
623  I[60] = (T)(img)(_n2##x,_n2##y,z,c), I[61] = (T)(img)(_n3##x,_n2##y,z,c), I[62] = (T)(img)(_n4##x,_n2##y,z,c), I[63] = (T)(img)(_p4##x,_n3##y,z,c), \
624  I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), \
625  I[68] = (T)(img)(_n1##x,_n3##y,z,c), I[69] = (T)(img)(_n2##x,_n3##y,z,c), I[70] = (T)(img)(_n3##x,_n3##y,z,c), I[71] = (T)(img)(_n4##x,_n3##y,z,c), \
626  I[72] = (T)(img)(_p4##x,_n4##y,z,c), I[73] = (T)(img)(_p3##x,_n4##y,z,c), I[74] = (T)(img)(_p2##x,_n4##y,z,c), I[75] = (T)(img)(_p1##x,_n4##y,z,c), \
627  I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), \
628  I[80] = (T)(img)(_n4##x,_n4##y,z,c)
629 
630 #define cimg_get2x2x2(img,x,y,z,c,I,T) \
631  I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), I[3] = (T)(img)(_n1##x,_n1##y,z,c), \
632  I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
633 
634 #define cimg_get3x3x3(img,x,y,z,c,I,T) \
635  I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), \
636  I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), I[5] = (T)(img)(_n1##x,y,_p1##z,c), \
637  I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), \
638  I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), I[11] = (T)(img)(_n1##x,_p1##y,z,c), \
639  I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), I[14] = (T)(img)(_n1##x,y,z,c), \
640  I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), I[17] = (T)(img)(_n1##x,_n1##y,z,c), \
641  I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), \
642  I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), I[23] = (T)(img)(_n1##x,y,_n1##z,c), \
643  I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
644 
645 // Macros to perform various image loops.
646 //
647 // These macros are simpler to use than loops with C++ iterators.
648 #define cimg_for(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs)
649 #define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size(); (ptrs--)>(img)._data; )
650 #define cimg_foroff(img,off) for (unsigned long off = 0, _max##off = (img).size(); off<_max##off; ++off)
651 
652 #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
653 #define cimg_forX(img,x) cimg_for1((img)._width,x)
654 #define cimg_forY(img,y) cimg_for1((img)._height,y)
655 #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
656 #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
657 #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
658 #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
659 #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
660 #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
661 #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
662 #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
663 #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
664 #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
665 #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
666 #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
667 #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
668 
669 #define cimg_for_in1(bound,i0,i1,i) \
670  for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound)-1; i<=_max##i; ++i)
671 #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
672 #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
673 #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
674 #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
675 #define cimg_for_inXY(img,x0,y0,x1,y1,x,y) cimg_for_inY(img,y0,y1,y) cimg_for_inX(img,x0,x1,x)
676 #define cimg_for_inXZ(img,x0,z0,x1,z1,x,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inX(img,x0,x1,x)
677 #define cimg_for_inXC(img,x0,c0,x1,c1,x,c) cimg_for_inC(img,c0,c1,c) cimg_for_inX(img,x0,x1,x)
678 #define cimg_for_inYZ(img,y0,z0,y1,z1,y,z) cimg_for_inZ(img,x0,z1,z) cimg_for_inY(img,y0,y1,y)
679 #define cimg_for_inYC(img,y0,c0,y1,c1,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inY(img,y0,y1,y)
680 #define cimg_for_inZC(img,z0,c0,z1,c1,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inZ(img,z0,z1,z)
681 #define cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inXY(img,x0,y0,x1,y1,x,y)
682 #define cimg_for_inXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXY(img,x0,y0,x1,y1,x,y)
683 #define cimg_for_inXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXZ(img,x0,z0,x1,z1,x,z)
684 #define cimg_for_inYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inYZ(img,y0,z0,y1,z1,y,z)
685 #define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
686 #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width-1-(n),x)
687 #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height-1-(n),y)
688 #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth-1-(n),z)
689 #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum-1-(n),c)
690 #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
691 #define cimg_for_insideXYZ(img,x,y,z,n) cimg_for_inXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
692 #define cimg_for_insideXYZC(img,x,y,z,c,n) cimg_for_inXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
693 
694 #define cimg_for_out1(boundi,i0,i1,i) \
695  for (int i = (int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1)+1:i)
696 #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
697  for (int j = 0; j<(int)(boundj); ++j) \
698  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
699  ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1)+1:i))
700 #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
701  for (int k = 0; k<(int)(boundk); ++k) \
702  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
703  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
704  ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1)+1:i))
705 #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
706  for (int l = 0; l<(int)(boundl); ++l) \
707  for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
708  for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
709  for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1)+1; i<(int)(boundi); \
710  ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1)+1:i))
711 #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
712 #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
713 #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
714 #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
715 #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
716 #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
717 #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
718 #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
719 #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
720 #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
721 #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z)
722 #define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c)
723 #define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c)
724 #define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c)
725 #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
726  cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
727 #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width-1-(n),x)
728 #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height-1-(n),y)
729 #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth-1-(n),z)
730 #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum-1-(n),c)
731 #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width-1-(n),(img)._height-1-(n),x,y)
732 #define cimg_for_borderXYZ(img,x,y,z,n) cimg_for_outXYZ(img,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),x,y,z)
733 #define cimg_for_borderXYZC(img,x,y,z,c,n) \
734  cimg_for_outXYZC(img,n,n,n,n,(img)._width-1-(n),(img)._height-1-(n),(img)._depth-1-(n),(img)._spectrum-1-(n),x,y,z,c)
735 
736 #define cimg_for_spiralXY(img,x,y) \
737  for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
738  --_n1##y, _n1##x+=(_n1##x>>2)-((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width-1-++x:((_n1##x&3)==2?(img)._height-1-++y:--x))))?0:1)
739 
740 #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
741  for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
742  _dx=(x1)>(x0)?(int)(x1)-(int)(x0):(_sx=-1,(int)(x0)-(int)(x1)), \
743  _dy=(y1)>(y0)?(int)(y1)-(int)(y0):(_sy=-1,(int)(y0)-(int)(y1)), \
744  _counter = _dx, \
745  _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
746  _counter>=0; \
747  --_counter, x+=_steep? \
748  (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
749  (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
750 
751 #define cimg_for2(bound,i) \
752  for (int i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1; \
753  _n1##i<(int)(bound) || i==--_n1##i; \
754  ++i, ++_n1##i)
755 #define cimg_for2X(img,x) cimg_for2((img)._width,x)
756 #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
757 #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
758 #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
759 #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
760 #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
761 #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
762 #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
763 #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
764 #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
765 #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
766 #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
767 #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
768 #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
769 
770 #define cimg_for_in2(bound,i0,i1,i) \
771  for (int i = (int)(i0)<0?0:(int)(i0), \
772  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
773  i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
774  ++i, ++_n1##i)
775 #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
776 #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
777 #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
778 #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
779 #define cimg_for_in2XY(img,x0,y0,x1,y1,x,y) cimg_for_in2Y(img,y0,y1,y) cimg_for_in2X(img,x0,x1,x)
780 #define cimg_for_in2XZ(img,x0,z0,x1,z1,x,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2X(img,x0,x1,x)
781 #define cimg_for_in2XC(img,x0,c0,x1,c1,x,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2X(img,x0,x1,x)
782 #define cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2Y(img,y0,y1,y)
783 #define cimg_for_in2YC(img,y0,c0,y1,c1,y,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Y(img,y0,y1,y)
784 #define cimg_for_in2ZC(img,z0,c0,z1,c1,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Z(img,z0,z1,z)
785 #define cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2XY(img,x0,y0,x1,y1,x,y)
786 #define cimg_for_in2XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z)
787 #define cimg_for_in2YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z)
788 #define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
789 
790 #define cimg_for3(bound,i) \
791  for (int i = 0, _p1##i = 0, \
792  _n1##i = 1>=(bound)?(int)(bound)-1:1; \
793  _n1##i<(int)(bound) || i==--_n1##i; \
794  _p1##i = i++, ++_n1##i)
795 #define cimg_for3X(img,x) cimg_for3((img)._width,x)
796 #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
797 #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
798 #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
799 #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
800 #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
801 #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
802 #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
803 #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
804 #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
805 #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
806 #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
807 #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
808 #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
809 
810 #define cimg_for_in3(bound,i0,i1,i) \
811  for (int i = (int)(i0)<0?0:(int)(i0), \
812  _p1##i = i-1<0?0:i-1, \
813  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1; \
814  i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
815  _p1##i = i++, ++_n1##i)
816 #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
817 #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
818 #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
819 #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
820 #define cimg_for_in3XY(img,x0,y0,x1,y1,x,y) cimg_for_in3Y(img,y0,y1,y) cimg_for_in3X(img,x0,x1,x)
821 #define cimg_for_in3XZ(img,x0,z0,x1,z1,x,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3X(img,x0,x1,x)
822 #define cimg_for_in3XC(img,x0,c0,x1,c1,x,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3X(img,x0,x1,x)
823 #define cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3Y(img,y0,y1,y)
824 #define cimg_for_in3YC(img,y0,c0,y1,c1,y,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Y(img,y0,y1,y)
825 #define cimg_for_in3ZC(img,z0,c0,z1,c1,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Z(img,z0,z1,z)
826 #define cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3XY(img,x0,y0,x1,y1,x,y)
827 #define cimg_for_in3XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z)
828 #define cimg_for_in3YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z)
829 #define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
830 
831 #define cimg_for4(bound,i) \
832  for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound)-1:1, \
833  _n2##i = 2>=(bound)?(int)(bound)-1:2; \
834  _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
835  _p1##i = i++, ++_n1##i, ++_n2##i)
836 #define cimg_for4X(img,x) cimg_for4((img)._width,x)
837 #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
838 #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
839 #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
840 #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
841 #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
842 #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
843 #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
844 #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
845 #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
846 #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
847 #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
848 #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
849 #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
850 
851 #define cimg_for_in4(bound,i0,i1,i) \
852  for (int i = (int)(i0)<0?0:(int)(i0), \
853  _p1##i = i-1<0?0:i-1, \
854  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
855  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
856  i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
857  _p1##i = i++, ++_n1##i, ++_n2##i)
858 #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
859 #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
860 #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
861 #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
862 #define cimg_for_in4XY(img,x0,y0,x1,y1,x,y) cimg_for_in4Y(img,y0,y1,y) cimg_for_in4X(img,x0,x1,x)
863 #define cimg_for_in4XZ(img,x0,z0,x1,z1,x,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4X(img,x0,x1,x)
864 #define cimg_for_in4XC(img,x0,c0,x1,c1,x,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4X(img,x0,x1,x)
865 #define cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4Y(img,y0,y1,y)
866 #define cimg_for_in4YC(img,y0,c0,y1,c1,y,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Y(img,y0,y1,y)
867 #define cimg_for_in4ZC(img,z0,c0,z1,c1,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Z(img,z0,z1,z)
868 #define cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4XY(img,x0,y0,x1,y1,x,y)
869 #define cimg_for_in4XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z)
870 #define cimg_for_in4YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z)
871 #define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
872 
873 #define cimg_for5(bound,i) \
874  for (int i = 0, _p2##i = 0, _p1##i = 0, \
875  _n1##i = 1>=(bound)?(int)(bound)-1:1, \
876  _n2##i = 2>=(bound)?(int)(bound)-1:2; \
877  _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
878  _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
879 #define cimg_for5X(img,x) cimg_for5((img)._width,x)
880 #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
881 #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
882 #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
883 #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
884 #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
885 #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
886 #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
887 #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
888 #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
889 #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
890 #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
891 #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
892 #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
893 
894 #define cimg_for_in5(bound,i0,i1,i) \
895  for (int i = (int)(i0)<0?0:(int)(i0), \
896  _p2##i = i-2<0?0:i-2, \
897  _p1##i = i-1<0?0:i-1, \
898  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
899  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2; \
900  i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
901  _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
902 #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
903 #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
904 #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
905 #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
906 #define cimg_for_in5XY(img,x0,y0,x1,y1,x,y) cimg_for_in5Y(img,y0,y1,y) cimg_for_in5X(img,x0,x1,x)
907 #define cimg_for_in5XZ(img,x0,z0,x1,z1,x,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5X(img,x0,x1,x)
908 #define cimg_for_in5XC(img,x0,c0,x1,c1,x,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5X(img,x0,x1,x)
909 #define cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5Y(img,y0,y1,y)
910 #define cimg_for_in5YC(img,y0,c0,y1,c1,y,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Y(img,y0,y1,y)
911 #define cimg_for_in5ZC(img,z0,c0,z1,c1,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Z(img,z0,z1,z)
912 #define cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5XY(img,x0,y0,x1,y1,x,y)
913 #define cimg_for_in5XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z)
914 #define cimg_for_in5YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z)
915 #define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
916 
917 #define cimg_for6(bound,i) \
918  for (int i = 0, _p2##i = 0, _p1##i = 0, \
919  _n1##i = 1>=(bound)?(int)(bound)-1:1, \
920  _n2##i = 2>=(bound)?(int)(bound)-1:2, \
921  _n3##i = 3>=(bound)?(int)(bound)-1:3; \
922  _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
923  _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
924 #define cimg_for6X(img,x) cimg_for6((img)._width,x)
925 #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
926 #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
927 #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
928 #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
929 #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
930 #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
931 #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
932 #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
933 #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
934 #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
935 #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
936 #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
937 #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
938 
939 #define cimg_for_in6(bound,i0,i1,i) \
940  for (int i = (int)(i0)<0?0:(int)(i0), \
941  _p2##i = i-2<0?0:i-2, \
942  _p1##i = i-1<0?0:i-1, \
943  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
944  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
945  _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
946  i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
947  _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
948 #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
949 #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
950 #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
951 #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
952 #define cimg_for_in6XY(img,x0,y0,x1,y1,x,y) cimg_for_in6Y(img,y0,y1,y) cimg_for_in6X(img,x0,x1,x)
953 #define cimg_for_in6XZ(img,x0,z0,x1,z1,x,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6X(img,x0,x1,x)
954 #define cimg_for_in6XC(img,x0,c0,x1,c1,x,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6X(img,x0,x1,x)
955 #define cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6Y(img,y0,y1,y)
956 #define cimg_for_in6YC(img,y0,c0,y1,c1,y,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Y(img,y0,y1,y)
957 #define cimg_for_in6ZC(img,z0,c0,z1,c1,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Z(img,z0,z1,z)
958 #define cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6XY(img,x0,y0,x1,y1,x,y)
959 #define cimg_for_in6XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z)
960 #define cimg_for_in6YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z)
961 #define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
962 
963 #define cimg_for7(bound,i) \
964  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
965  _n1##i = 1>=(bound)?(int)(bound)-1:1, \
966  _n2##i = 2>=(bound)?(int)(bound)-1:2, \
967  _n3##i = 3>=(bound)?(int)(bound)-1:3; \
968  _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
969  _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
970 #define cimg_for7X(img,x) cimg_for7((img)._width,x)
971 #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
972 #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
973 #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
974 #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
975 #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
976 #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
977 #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
978 #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
979 #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
980 #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
981 #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
982 #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
983 #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
984 
985 #define cimg_for_in7(bound,i0,i1,i) \
986  for (int i = (int)(i0)<0?0:(int)(i0), \
987  _p3##i = i-3<0?0:i-3, \
988  _p2##i = i-2<0?0:i-2, \
989  _p1##i = i-1<0?0:i-1, \
990  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
991  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
992  _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3; \
993  i<=(int)(i1) && (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
994  _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
995 #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
996 #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
997 #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
998 #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
999 #define cimg_for_in7XY(img,x0,y0,x1,y1,x,y) cimg_for_in7Y(img,y0,y1,y) cimg_for_in7X(img,x0,x1,x)
1000 #define cimg_for_in7XZ(img,x0,z0,x1,z1,x,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7X(img,x0,x1,x)
1001 #define cimg_for_in7XC(img,x0,c0,x1,c1,x,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7X(img,x0,x1,x)
1002 #define cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7Y(img,y0,y1,y)
1003 #define cimg_for_in7YC(img,y0,c0,y1,c1,y,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Y(img,y0,y1,y)
1004 #define cimg_for_in7ZC(img,z0,c0,z1,c1,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Z(img,z0,z1,z)
1005 #define cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7XY(img,x0,y0,x1,y1,x,y)
1006 #define cimg_for_in7XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z)
1007 #define cimg_for_in7YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z)
1008 #define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1009 
1010 #define cimg_for8(bound,i) \
1011  for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1012  _n1##i = 1>=(bound)?(int)(bound)-1:1, \
1013  _n2##i = 2>=(bound)?(int)(bound)-1:2, \
1014  _n3##i = 3>=(bound)?(int)(bound)-1:3, \
1015  _n4##i = 4>=(bound)?(int)(bound)-1:4; \
1016  _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1017  i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1018  _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1019 #define cimg_for8X(img,x) cimg_for8((img)._width,x)
1020 #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
1021 #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
1022 #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
1023 #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
1024 #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
1025 #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
1026 #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
1027 #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
1028 #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
1029 #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
1030 #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
1031 #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
1032 #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
1033 
1034 #define cimg_for_in8(bound,i0,i1,i) \
1035  for (int i = (int)(i0)<0?0:(int)(i0), \
1036  _p3##i = i-3<0?0:i-3, \
1037  _p2##i = i-2<0?0:i-2, \
1038  _p1##i = i-1<0?0:i-1, \
1039  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1040  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1041  _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
1042  _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
1043  i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1044  i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1045  _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1046 #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
1047 #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
1048 #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
1049 #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
1050 #define cimg_for_in8XY(img,x0,y0,x1,y1,x,y) cimg_for_in8Y(img,y0,y1,y) cimg_for_in8X(img,x0,x1,x)
1051 #define cimg_for_in8XZ(img,x0,z0,x1,z1,x,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8X(img,x0,x1,x)
1052 #define cimg_for_in8XC(img,x0,c0,x1,c1,x,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8X(img,x0,x1,x)
1053 #define cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8Y(img,y0,y1,y)
1054 #define cimg_for_in8YC(img,y0,c0,y1,c1,y,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Y(img,y0,y1,y)
1055 #define cimg_for_in8ZC(img,z0,c0,z1,c1,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Z(img,z0,z1,z)
1056 #define cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8XY(img,x0,y0,x1,y1,x,y)
1057 #define cimg_for_in8XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z)
1058 #define cimg_for_in8YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z)
1059 #define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1060 
1061 #define cimg_for9(bound,i) \
1062  for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
1063  _n1##i = 1>=(int)(bound)?(int)(bound)-1:1, \
1064  _n2##i = 2>=(int)(bound)?(int)(bound)-1:2, \
1065  _n3##i = 3>=(int)(bound)?(int)(bound)-1:3, \
1066  _n4##i = 4>=(int)(bound)?(int)(bound)-1:4; \
1067  _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1068  i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
1069  _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1070 #define cimg_for9X(img,x) cimg_for9((img)._width,x)
1071 #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
1072 #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
1073 #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
1074 #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
1075 #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
1076 #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
1077 #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
1078 #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
1079 #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
1080 #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
1081 #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
1082 #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
1083 #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
1084 
1085 #define cimg_for_in9(bound,i0,i1,i) \
1086  for (int i = (int)(i0)<0?0:(int)(i0), \
1087  _p4##i = i-4<0?0:i-4, \
1088  _p3##i = i-3<0?0:i-3, \
1089  _p2##i = i-2<0?0:i-2, \
1090  _p1##i = i-1<0?0:i-1, \
1091  _n1##i = i+1>=(int)(bound)?(int)(bound)-1:i+1, \
1092  _n2##i = i+2>=(int)(bound)?(int)(bound)-1:i+2, \
1093  _n3##i = i+3>=(int)(bound)?(int)(bound)-1:i+3, \
1094  _n4##i = i+4>=(int)(bound)?(int)(bound)-1:i+4; \
1095  i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
1096  i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
1097  _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
1098 #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
1099 #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
1100 #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
1101 #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
1102 #define cimg_for_in9XY(img,x0,y0,x1,y1,x,y) cimg_for_in9Y(img,y0,y1,y) cimg_for_in9X(img,x0,x1,x)
1103 #define cimg_for_in9XZ(img,x0,z0,x1,z1,x,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9X(img,x0,x1,x)
1104 #define cimg_for_in9XC(img,x0,c0,x1,c1,x,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9X(img,x0,x1,x)
1105 #define cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9Y(img,y0,y1,y)
1106 #define cimg_for_in9YC(img,y0,c0,y1,c1,y,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Y(img,y0,y1,y)
1107 #define cimg_for_in9ZC(img,z0,c0,z1,c1,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Z(img,z0,z1,z)
1108 #define cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9XY(img,x0,y0,x1,y1,x,y)
1109 #define cimg_for_in9XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z)
1110 #define cimg_for_in9YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z)
1111 #define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
1112 
1113 #define cimg_for2x2(img,x,y,z,c,I,T) \
1114  cimg_for2((img)._height,y) for (int x = 0, \
1115  _n1##x = (int)( \
1116  (I[0] = (T)(img)(0,y,z,c)), \
1117  (I[2] = (T)(img)(0,_n1##y,z,c)), \
1118  1>=(img)._width?(img).width()-1:1); \
1119  (_n1##x<(img).width() && ( \
1120  (I[1] = (T)(img)(_n1##x,y,z,c)), \
1121  (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1122  x==--_n1##x; \
1123  I[0] = I[1], \
1124  I[2] = I[3], \
1125  ++x, ++_n1##x)
1126 
1127 #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1128  cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1129  _n1##x = (int)( \
1130  (I[0] = (T)(img)(x,y,z,c)), \
1131  (I[2] = (T)(img)(x,_n1##y,z,c)), \
1132  x+1>=(int)(img)._width?(img).width()-1:x+1); \
1133  x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1134  (I[1] = (T)(img)(_n1##x,y,z,c)), \
1135  (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1136  x==--_n1##x); \
1137  I[0] = I[1], \
1138  I[2] = I[3], \
1139  ++x, ++_n1##x)
1140 
1141 #define cimg_for3x3(img,x,y,z,c,I,T) \
1142  cimg_for3((img)._height,y) for (int x = 0, \
1143  _p1##x = 0, \
1144  _n1##x = (int)( \
1145  (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1146  (I[3] = I[4] = (T)(img)(0,y,z,c)), \
1147  (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
1148  1>=(img)._width?(img).width()-1:1); \
1149  (_n1##x<(img).width() && ( \
1150  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1151  (I[5] = (T)(img)(_n1##x,y,z,c)), \
1152  (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1153  x==--_n1##x; \
1154  I[0] = I[1], I[1] = I[2], \
1155  I[3] = I[4], I[4] = I[5], \
1156  I[6] = I[7], I[7] = I[8], \
1157  _p1##x = x++, ++_n1##x)
1158 
1159 #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1160  cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1161  _p1##x = x-1<0?0:x-1, \
1162  _n1##x = (int)( \
1163  (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1164  (I[3] = (T)(img)(_p1##x,y,z,c)), \
1165  (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
1166  (I[1] = (T)(img)(x,_p1##y,z,c)), \
1167  (I[4] = (T)(img)(x,y,z,c)), \
1168  (I[7] = (T)(img)(x,_n1##y,z,c)), \
1169  x+1>=(int)(img)._width?(img).width()-1:x+1); \
1170  x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1171  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1172  (I[5] = (T)(img)(_n1##x,y,z,c)), \
1173  (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
1174  x==--_n1##x); \
1175  I[0] = I[1], I[1] = I[2], \
1176  I[3] = I[4], I[4] = I[5], \
1177  I[6] = I[7], I[7] = I[8], \
1178  _p1##x = x++, ++_n1##x)
1179 
1180 #define cimg_for4x4(img,x,y,z,c,I,T) \
1181  cimg_for4((img)._height,y) for (int x = 0, \
1182  _p1##x = 0, \
1183  _n1##x = 1>=(img)._width?(img).width()-1:1, \
1184  _n2##x = (int)( \
1185  (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
1186  (I[4] = I[5] = (T)(img)(0,y,z,c)), \
1187  (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
1188  (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
1189  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1190  (I[6] = (T)(img)(_n1##x,y,z,c)), \
1191  (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1192  (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1193  2>=(img)._width?(img).width()-1:2); \
1194  (_n2##x<(img).width() && ( \
1195  (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1196  (I[7] = (T)(img)(_n2##x,y,z,c)), \
1197  (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1198  (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1199  _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1200  I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1201  I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1202  I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1203  I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1204  _p1##x = x++, ++_n1##x, ++_n2##x)
1205 
1206 #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1207  cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1208  _p1##x = x-1<0?0:x-1, \
1209  _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1210  _n2##x = (int)( \
1211  (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
1212  (I[4] = (T)(img)(_p1##x,y,z,c)), \
1213  (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
1214  (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
1215  (I[1] = (T)(img)(x,_p1##y,z,c)), \
1216  (I[5] = (T)(img)(x,y,z,c)), \
1217  (I[9] = (T)(img)(x,_n1##y,z,c)), \
1218  (I[13] = (T)(img)(x,_n2##y,z,c)), \
1219  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
1220  (I[6] = (T)(img)(_n1##x,y,z,c)), \
1221  (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
1222  (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
1223  x+2>=(int)(img)._width?(img).width()-1:x+2); \
1224  x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1225  (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
1226  (I[7] = (T)(img)(_n2##x,y,z,c)), \
1227  (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
1228  (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1229  _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1230  I[0] = I[1], I[1] = I[2], I[2] = I[3], \
1231  I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1232  I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1233  I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1234  _p1##x = x++, ++_n1##x, ++_n2##x)
1235 
1236 #define cimg_for5x5(img,x,y,z,c,I,T) \
1237  cimg_for5((img)._height,y) for (int x = 0, \
1238  _p2##x = 0, _p1##x = 0, \
1239  _n1##x = 1>=(img)._width?(img).width()-1:1, \
1240  _n2##x = (int)( \
1241  (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1242  (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
1243  (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
1244  (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
1245  (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
1246  (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1247  (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1248  (I[13] = (T)(img)(_n1##x,y,z,c)), \
1249  (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1250  (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
1251  2>=(img)._width?(img).width()-1:2); \
1252  (_n2##x<(img).width() && ( \
1253  (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1254  (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1255  (I[14] = (T)(img)(_n2##x,y,z,c)), \
1256  (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1257  (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1258  _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
1259  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1260  I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1261  I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1262  I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1263  I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1264  _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1265 
1266 #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1267  cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1268  _p2##x = x-2<0?0:x-2, \
1269  _p1##x = x-1<0?0:x-1, \
1270  _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1271  _n2##x = (int)( \
1272  (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1273  (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
1274  (I[10] = (T)(img)(_p2##x,y,z,c)), \
1275  (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
1276  (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
1277  (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1278  (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
1279  (I[11] = (T)(img)(_p1##x,y,z,c)), \
1280  (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
1281  (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
1282  (I[2] = (T)(img)(x,_p2##y,z,c)), \
1283  (I[7] = (T)(img)(x,_p1##y,z,c)), \
1284  (I[12] = (T)(img)(x,y,z,c)), \
1285  (I[17] = (T)(img)(x,_n1##y,z,c)), \
1286  (I[22] = (T)(img)(x,_n2##y,z,c)), \
1287  (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1288  (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
1289  (I[13] = (T)(img)(_n1##x,y,z,c)), \
1290  (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
1291  (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
1292  x+2>=(int)(img)._width?(img).width()-1:x+2); \
1293  x<=(int)(x1) && ((_n2##x<(img).width() && ( \
1294  (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1295  (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
1296  (I[14] = (T)(img)(_n2##x,y,z,c)), \
1297  (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
1298  (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
1299  _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
1300  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
1301  I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
1302  I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
1303  I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
1304  I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
1305  _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
1306 
1307 #define cimg_for6x6(img,x,y,z,c,I,T) \
1308  cimg_for6((img)._height,y) for (int x = 0, \
1309  _p2##x = 0, _p1##x = 0, \
1310  _n1##x = 1>=(img)._width?(img).width()-1:1, \
1311  _n2##x = 2>=(img)._width?(img).width()-1:2, \
1312  _n3##x = (int)( \
1313  (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
1314  (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
1315  (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
1316  (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
1317  (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
1318  (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
1319  (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1320  (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1321  (I[15] = (T)(img)(_n1##x,y,z,c)), \
1322  (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1323  (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1324  (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1325  (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1326  (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1327  (I[16] = (T)(img)(_n2##x,y,z,c)), \
1328  (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1329  (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1330  (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1331  3>=(img)._width?(img).width()-1:3); \
1332  (_n3##x<(img).width() && ( \
1333  (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1334  (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1335  (I[17] = (T)(img)(_n3##x,y,z,c)), \
1336  (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1337  (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1338  (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1339  _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
1340  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1341  I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1342  I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1343  I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1344  I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1345  I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1346  _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1347 
1348 #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1349  cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
1350  _p2##x = x-2<0?0:x-2, \
1351  _p1##x = x-1<0?0:x-1, \
1352  _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1353  _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
1354  _n3##x = (int)( \
1355  (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
1356  (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
1357  (I[12] = (T)(img)(_p2##x,y,z,c)), \
1358  (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
1359  (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
1360  (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
1361  (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
1362  (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
1363  (I[13] = (T)(img)(_p1##x,y,z,c)), \
1364  (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
1365  (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
1366  (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
1367  (I[2] = (T)(img)(x,_p2##y,z,c)), \
1368  (I[8] = (T)(img)(x,_p1##y,z,c)), \
1369  (I[14] = (T)(img)(x,y,z,c)), \
1370  (I[20] = (T)(img)(x,_n1##y,z,c)), \
1371  (I[26] = (T)(img)(x,_n2##y,z,c)), \
1372  (I[32] = (T)(img)(x,_n3##y,z,c)), \
1373  (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
1374  (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
1375  (I[15] = (T)(img)(_n1##x,y,z,c)), \
1376  (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
1377  (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
1378  (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
1379  (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
1380  (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
1381  (I[16] = (T)(img)(_n2##x,y,z,c)), \
1382  (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
1383  (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
1384  (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
1385  x+3>=(int)(img)._width?(img).width()-1:x+3); \
1386  x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1387  (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
1388  (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
1389  (I[17] = (T)(img)(_n3##x,y,z,c)), \
1390  (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
1391  (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
1392  (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1393  _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
1394  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
1395  I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
1396  I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1397  I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1398  I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
1399  I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1400  _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1401 
1402 #define cimg_for7x7(img,x,y,z,c,I,T) \
1403  cimg_for7((img)._height,y) for (int x = 0, \
1404  _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1405  _n1##x = 1>=(img)._width?(img).width()-1:1, \
1406  _n2##x = 2>=(img)._width?(img).width()-1:2, \
1407  _n3##x = (int)( \
1408  (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1409  (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
1410  (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
1411  (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
1412  (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
1413  (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
1414  (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
1415  (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1416  (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1417  (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1418  (I[25] = (T)(img)(_n1##x,y,z,c)), \
1419  (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1420  (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1421  (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1422  (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1423  (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1424  (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1425  (I[26] = (T)(img)(_n2##x,y,z,c)), \
1426  (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1427  (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1428  (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1429  3>=(img)._width?(img).width()-1:3); \
1430  (_n3##x<(img).width() && ( \
1431  (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1432  (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1433  (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1434  (I[27] = (T)(img)(_n3##x,y,z,c)), \
1435  (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1436  (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1437  (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1438  _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
1439  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1440  I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1441  I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1442  I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1443  I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1444  I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1445  I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1446  _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1447 
1448 #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1449  cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1450  _p3##x = x-3<0?0:x-3, \
1451  _p2##x = x-2<0?0:x-2, \
1452  _p1##x = x-1<0?0:x-1, \
1453  _n1##x = x+1>=(int)(img)._width?(img).width()-1:x+1, \
1454  _n2##x = x+2>=(int)(img)._width?(img).width()-1:x+2, \
1455  _n3##x = (int)( \
1456  (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1457  (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
1458  (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
1459  (I[21] = (T)(img)(_p3##x,y,z,c)), \
1460  (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
1461  (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
1462  (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
1463  (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1464  (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
1465  (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
1466  (I[22] = (T)(img)(_p2##x,y,z,c)), \
1467  (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
1468  (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
1469  (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
1470  (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1471  (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
1472  (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
1473  (I[23] = (T)(img)(_p1##x,y,z,c)), \
1474  (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
1475  (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
1476  (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
1477  (I[3] = (T)(img)(x,_p3##y,z,c)), \
1478  (I[10] = (T)(img)(x,_p2##y,z,c)), \
1479  (I[17] = (T)(img)(x,_p1##y,z,c)), \
1480  (I[24] = (T)(img)(x,y,z,c)), \
1481  (I[31] = (T)(img)(x,_n1##y,z,c)), \
1482  (I[38] = (T)(img)(x,_n2##y,z,c)), \
1483  (I[45] = (T)(img)(x,_n3##y,z,c)), \
1484  (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1485  (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
1486  (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
1487  (I[25] = (T)(img)(_n1##x,y,z,c)), \
1488  (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
1489  (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
1490  (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
1491  (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1492  (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
1493  (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
1494  (I[26] = (T)(img)(_n2##x,y,z,c)), \
1495  (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
1496  (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
1497  (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
1498  x+3>=(int)(img)._width?(img).width()-1:x+3); \
1499  x<=(int)(x1) && ((_n3##x<(img).width() && ( \
1500  (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1501  (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
1502  (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
1503  (I[27] = (T)(img)(_n3##x,y,z,c)), \
1504  (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
1505  (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
1506  (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
1507  _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
1508  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
1509  I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
1510  I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
1511  I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
1512  I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
1513  I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
1514  I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
1515  _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
1516 
1517 #define cimg_for8x8(img,x,y,z,c,I,T) \
1518  cimg_for8((img)._height,y) for (int x = 0, \
1519  _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1520  _n1##x = 1>=((img)._width)?(img).width()-1:1, \
1521  _n2##x = 2>=((img)._width)?(img).width()-1:2, \
1522  _n3##x = 3>=((img)._width)?(img).width()-1:3, \
1523  _n4##x = (int)( \
1524  (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
1525  (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
1526  (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
1527  (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
1528  (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
1529  (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
1530  (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
1531  (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
1532  (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1533  (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1534  (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1535  (I[28] = (T)(img)(_n1##x,y,z,c)), \
1536  (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1537  (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1538  (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1539  (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1540  (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1541  (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1542  (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1543  (I[29] = (T)(img)(_n2##x,y,z,c)), \
1544  (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1545  (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1546  (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1547  (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1548  (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1549  (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1550  (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1551  (I[30] = (T)(img)(_n3##x,y,z,c)), \
1552  (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1553  (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1554  (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1555  (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1556  4>=((img)._width)?(img).width()-1:4); \
1557  (_n4##x<(img).width() && ( \
1558  (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1559  (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1560  (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1561  (I[31] = (T)(img)(_n4##x,y,z,c)), \
1562  (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1563  (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1564  (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1565  (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1566  _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1567  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1568  I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1569  I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1570  I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \
1571  I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \
1572  I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \
1573  I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \
1574  I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \
1575  _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1576 
1577 #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1578  cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1579  _p3##x = x-3<0?0:x-3, \
1580  _p2##x = x-2<0?0:x-2, \
1581  _p1##x = x-1<0?0:x-1, \
1582  _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
1583  _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
1584  _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
1585  _n4##x = (int)( \
1586  (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
1587  (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
1588  (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
1589  (I[24] = (T)(img)(_p3##x,y,z,c)), \
1590  (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
1591  (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
1592  (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
1593  (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
1594  (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
1595  (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
1596  (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
1597  (I[25] = (T)(img)(_p2##x,y,z,c)), \
1598  (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
1599  (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
1600  (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
1601  (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
1602  (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
1603  (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
1604  (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
1605  (I[26] = (T)(img)(_p1##x,y,z,c)), \
1606  (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
1607  (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
1608  (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
1609  (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
1610  (I[3] = (T)(img)(x,_p3##y,z,c)), \
1611  (I[11] = (T)(img)(x,_p2##y,z,c)), \
1612  (I[19] = (T)(img)(x,_p1##y,z,c)), \
1613  (I[27] = (T)(img)(x,y,z,c)), \
1614  (I[35] = (T)(img)(x,_n1##y,z,c)), \
1615  (I[43] = (T)(img)(x,_n2##y,z,c)), \
1616  (I[51] = (T)(img)(x,_n3##y,z,c)), \
1617  (I[59] = (T)(img)(x,_n4##y,z,c)), \
1618  (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
1619  (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
1620  (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
1621  (I[28] = (T)(img)(_n1##x,y,z,c)), \
1622  (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
1623  (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
1624  (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
1625  (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
1626  (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
1627  (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
1628  (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
1629  (I[29] = (T)(img)(_n2##x,y,z,c)), \
1630  (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
1631  (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
1632  (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
1633  (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
1634  (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
1635  (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
1636  (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
1637  (I[30] = (T)(img)(_n3##x,y,z,c)), \
1638  (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
1639  (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
1640  (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
1641  (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
1642  x+4>=(img).width()?(img).width()-1:x+4); \
1643  x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1644  (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
1645  (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
1646  (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
1647  (I[31] = (T)(img)(_n4##x,y,z,c)), \
1648  (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
1649  (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
1650  (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
1651  (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1652  _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1653  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \
1654  I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \
1655  I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
1656  I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \
1657  I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \
1658  I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \
1659  I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \
1660  I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \
1661  _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1662 
1663 #define cimg_for9x9(img,x,y,z,c,I,T) \
1664  cimg_for9((img)._height,y) for (int x = 0, \
1665  _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
1666  _n1##x = 1>=((img)._width)?(img).width()-1:1, \
1667  _n2##x = 2>=((img)._width)?(img).width()-1:2, \
1668  _n3##x = 3>=((img)._width)?(img).width()-1:3, \
1669  _n4##x = (int)( \
1670  (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \
1671  (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
1672  (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
1673  (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
1674  (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
1675  (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
1676  (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
1677  (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
1678  (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
1679  (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1680  (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1681  (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
1682  (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
1683  (I[41] = (T)(img)(_n1##x,y,z,c)), \
1684  (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
1685  (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
1686  (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
1687  (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
1688  (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
1689  (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
1690  (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
1691  (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
1692  (I[42] = (T)(img)(_n2##x,y,z,c)), \
1693  (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
1694  (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
1695  (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
1696  (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
1697  (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
1698  (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
1699  (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
1700  (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
1701  (I[43] = (T)(img)(_n3##x,y,z,c)), \
1702  (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
1703  (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
1704  (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
1705  (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
1706  4>=((img)._width)?(img).width()-1:4); \
1707  (_n4##x<(img).width() && ( \
1708  (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
1709  (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
1710  (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
1711  (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
1712  (I[44] = (T)(img)(_n4##x,y,z,c)), \
1713  (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
1714  (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
1715  (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
1716  (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1717  _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
1718  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \
1719  I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1720  I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \
1721  I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1722  I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \
1723  I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \
1724  I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \
1725  I[63] = I[64], I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \
1726  I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], I[79] = I[80], \
1727  _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1728 
1729 #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
1730  cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1731  _p4##x = x-4<0?0:x-4, \
1732  _p3##x = x-3<0?0:x-3, \
1733  _p2##x = x-2<0?0:x-2, \
1734  _p1##x = x-1<0?0:x-1, \
1735  _n1##x = x+1>=(img).width()?(img).width()-1:x+1, \
1736  _n2##x = x+2>=(img).width()?(img).width()-1:x+2, \
1737  _n3##x = x+3>=(img).width()?(img).width()-1:x+3, \
1738  _n4##x = (int)( \
1739  (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
1740  (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
1741  (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
1742  (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
1743  (I[36] = (T)(img)(_p4##x,y,z,c)), \
1744  (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
1745  (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
1746  (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
1747  (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
1748  (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
1749  (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
1750  (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
1751  (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
1752  (I[37] = (T)(img)(_p3##x,y,z,c)), \
1753  (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
1754  (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
1755  (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
1756  (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
1757  (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
1758  (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
1759  (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
1760  (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
1761  (I[38] = (T)(img)(_p2##x,y,z,c)), \
1762  (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
1763  (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
1764  (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
1765  (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
1766  (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
1767  (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
1768  (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
1769  (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
1770  (I[39] = (T)(img)(_p1##x,y,z,c)), \
1771  (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
1772  (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
1773  (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
1774  (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
1775  (I[4] = (T)(img)(x,_p4##y,z,c)), \
1776  (I[13] = (T)(img)(x,_p3##y,z,c)), \
1777  (I[22] = (T)(img)(x,_p2##y,z,c)), \
1778  (I[31] = (T)(img)(x,_p1##y,z,c)), \
1779  (I[40] = (T)(img)(x,y,z,c)), \
1780  (I[49] = (T)(img)(x,_n1##y,z,c)), \
1781  (I[58] = (T)(img)(x,_n2##y,z,c)), \
1782  (I[67] = (T)(img)(x,_n3##y,z,c)), \
1783  (I[76] = (T)(img)(x,_n4##y,z,c)), \
1784  (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
1785  (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
1786  (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
1787  (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
1788  (I[41] = (T)(img)(_n1##x,y,z,c)), \
1789  (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
1790  (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
1791  (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
1792  (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
1793  (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
1794  (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
1795  (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
1796  (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
1797  (I[42] = (T)(img)(_n2##x,y,z,c)), \
1798  (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
1799  (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
1800  (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
1801  (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
1802  (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
1803  (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
1804  (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
1805  (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
1806  (I[43] = (T)(img)(_n3##x,y,z,c)), \
1807  (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
1808  (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
1809  (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
1810  (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
1811  x+4>=(img).width()?(img).width()-1:x+4); \
1812  x<=(int)(x1) && ((_n4##x<(img).width() && ( \
1813  (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
1814  (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
1815  (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
1816  (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
1817  (I[44] = (T)(img)(_n4##x,y,z,c)), \
1818  (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
1819  (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
1820  (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
1821  (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
1822  _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
1823  I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \
1824  I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
1825  I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], \
1826  I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
1827  I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], \
1828  I[45] = I[46], I[46] = I[47], I[47] = I[48], I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], \
1829  I[54] = I[55], I[55] = I[56], I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], \
1830  I[63] = I[64], I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \
1831  I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], I[79] = I[80], \
1832  _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
1833 
1834 #define cimg_for2x2x2(img,x,y,z,c,I,T) \
1835  cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
1836  _n1##x = (int)( \
1837  (I[0] = (T)(img)(0,y,z,c)), \
1838  (I[2] = (T)(img)(0,_n1##y,z,c)), \
1839  (I[4] = (T)(img)(0,y,_n1##z,c)), \
1840  (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
1841  1>=(img)._width?(img).width()-1:1); \
1842  (_n1##x<(img).width() && ( \
1843  (I[1] = (T)(img)(_n1##x,y,z,c)), \
1844  (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
1845  (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
1846  (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1847  x==--_n1##x; \
1848  I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
1849  ++x, ++_n1##x)
1850 
1851 #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
1852  cimg_for_in2((img)._depth,z0,z1,z) cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1853  _n1##x = (int)( \
1854  (I[0] = (T)(img)(x,y,z,c)), \
1855  (I[2] = (T)(img)(x,_n1##y,z,c)), \
1856  (I[4] = (T)(img)(x,y,_n1##z,c)), \
1857  (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
1858  x+1>=(int)(img)._width?(img).width()-1:x+1); \
1859  x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1860  (I[1] = (T)(img)(_n1##x,y,z,c)), \
1861  (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
1862  (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
1863  (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1864  x==--_n1##x); \
1865  I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
1866  ++x, ++_n1##x)
1867 
1868 #define cimg_for3x3x3(img,x,y,z,c,I,T) \
1869  cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
1870  _p1##x = 0, \
1871  _n1##x = (int)( \
1872  (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
1873  (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \
1874  (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
1875  (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
1876  (I[12] = I[13] = (T)(img)(0,y,z,c)), \
1877  (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
1878  (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
1879  (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
1880  (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
1881  1>=(img)._width?(img).width()-1:1); \
1882  (_n1##x<(img).width() && ( \
1883  (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
1884  (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
1885  (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
1886  (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
1887  (I[14] = (T)(img)(_n1##x,y,z,c)), \
1888  (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
1889  (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
1890  (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
1891  (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1892  x==--_n1##x; \
1893  I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
1894  I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
1895  I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
1896  _p1##x = x++, ++_n1##x)
1897 
1898 #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
1899  cimg_for_in3((img)._depth,z0,z1,z) cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
1900  _p1##x = x-1<0?0:x-1, \
1901  _n1##x = (int)( \
1902  (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
1903  (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \
1904  (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
1905  (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
1906  (I[12] = (T)(img)(_p1##x,y,z,c)), \
1907  (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
1908  (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
1909  (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
1910  (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
1911  (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
1912  (I[4] = (T)(img)(x,y,_p1##z,c)), \
1913  (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
1914  (I[10] = (T)(img)(x,_p1##y,z,c)), \
1915  (I[13] = (T)(img)(x,y,z,c)), \
1916  (I[16] = (T)(img)(x,_n1##y,z,c)), \
1917  (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
1918  (I[22] = (T)(img)(x,y,_n1##z,c)), \
1919  (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
1920  x+1>=(int)(img)._width?(img).width()-1:x+1); \
1921  x<=(int)(x1) && ((_n1##x<(img).width() && ( \
1922  (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
1923  (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
1924  (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
1925  (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
1926  (I[14] = (T)(img)(_n1##x,y,z,c)), \
1927  (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
1928  (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
1929  (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
1930  (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
1931  x==--_n1##x); \
1932  I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
1933  I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
1934  I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
1935  _p1##x = x++, ++_n1##x)
1936 
1937 #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
1938 #define cimglist_for_in(list,l0,l1,l) \
1939  for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width-1; l<=_max##l; ++l)
1940 
1941 #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
1942 
1943 // Macros used to display error messages when exceptions are thrown.
1944 // You should not use these macros is your own code.
1945 #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
1946 #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
1947 #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
1948 #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
1949 #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
1950 #define cimglist_instance _width,_allocated_width,_data,pixel_type()
1951 
1952 /*------------------------------------------------
1953  #
1954  #
1955  # Define cimg_library:: namespace
1956  #
1957  #
1958  -------------------------------------------------*/
1960 
1971 namespace cimg_library_suffixed {
1972 
1973  // Declare the four classes of the CImg Library.
1974  template<typename T=float> struct CImg;
1975  template<typename T=float> struct CImgList;
1976  struct CImgDisplay;
1977  struct CImgException;
1978 
1979  // Declare cimg:: namespace.
1980  // This is an uncomplete namespace definition here. It only contains some
1981  // necessary stuffs to ensure a correct declaration order of the classes and functions
1982  // defined afterwards.
1983  namespace cimg {
1984 
1985  // Define ascii sequences for colored terminal output.
1986 #ifdef cimg_use_vt100
1987  const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
1988  const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 };
1989  const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 };
1990  const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
1991  const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 };
1992  const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 };
1993  const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
1994  const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 };
1995  const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 };
1996  const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
1997  const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 };
1998 #else
1999  const char t_normal[] = { 0 };
2000  const char *const t_black = cimg::t_normal,
2001  *const t_red = cimg::t_normal,
2002  *const t_green = cimg::t_normal,
2003  *const t_yellow = cimg::t_normal,
2004  *const t_blue = cimg::t_normal,
2005  *const t_magenta = cimg::t_normal,
2006  *const t_cyan = cimg::t_normal,
2007  *const t_white = cimg::t_normal,
2008  *const t_bold = cimg::t_normal,
2009  *const t_underscore = cimg::t_normal;
2010 #endif
2011 
2012  inline std::FILE* output(std::FILE *file=0);
2013  inline void info();
2014 
2016  template<typename T>
2017  inline void unused(const T&, ...) {}
2018 
2019  inline unsigned int& _exception_mode(const unsigned int value, const bool is_set) {
2020  static unsigned int mode = cimg_verbosity;
2021  if (is_set) mode = value;
2022  return mode;
2023  }
2024 
2026 
2035  inline unsigned int& exception_mode(const unsigned int mode) {
2036  return _exception_mode(mode,true);
2037  }
2038 
2040 
2043  inline unsigned int& exception_mode() {
2044  return _exception_mode(0,false);
2045  }
2046 
2047  inline int dialog(const char *const title, const char *const msg, const char *const button1_label="OK",
2048  const char *const button2_label=0, const char *const button3_label=0,
2049  const char *const button4_label=0, const char *const button5_label=0,
2050  const char *const button6_label=0, const bool centering=false);
2051 
2052  inline double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0);
2053  }
2054 
2055  /*---------------------------------------
2056  #
2057  # Define the CImgException structures
2058  #
2059  --------------------------------------*/
2061 
2119  struct CImgException : public std::exception {
2120 #define _cimg_exception_err(etype,disp_flag) \
2121  std::va_list ap; va_start(ap,format); cimg_vsnprintf(_message,sizeof(_message),format,ap); va_end(ap); \
2122  if (cimg::exception_mode()) { \
2123  std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
2124  if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } catch (CImgException&) {} \
2125  if (cimg::exception_mode()>=3) cimg_library_suffixed::cimg::info(); \
2126  }
2127 
2128  char _message[16384];
2129  CImgException() { *_message = 0; }
2130  CImgException(const char *const format, ...) { _cimg_exception_err("CImgException",true); }
2132  const char *what() const throw() { return _message; }
2133  };
2134 
2135  // The CImgInstanceException class is used to throw an exception related
2136  // to an invalid instance encountered in a library function call.
2138  CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
2139  };
2140 
2141  // The CImgArgumentException class is used to throw an exception related
2142  // to invalid arguments encountered in a library function call.
2144  CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
2145  };
2146 
2147  // The CImgIOException class is used to throw an exception related
2148  // to input/output file problems encountered in a library function call.
2149  struct CImgIOException : public CImgException {
2150  CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
2151  };
2152 
2153  // The CImgDisplayException class is used to throw an exception related
2154  // to display problems encountered in a library function call.
2156  CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); }
2157  };
2158 
2159  // The CImgWarningException class is used to throw an exception for warnings
2160  // encountered in a library function call.
2162  CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
2163  };
2164 
2165  /*-------------------------------------
2166  #
2167  # Define cimg:: namespace
2168  #
2169  -----------------------------------*/
2171 
2177  namespace cimg {
2178 
2179  // Define traits that will be used to determine the best data type to work in CImg functions.
2180  //
2181  template<typename T> struct type {
2182  static const char* string() {
2183  static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24",
2184  "unknown32", "unknown40", "unknown48", "unknown56",
2185  "unknown64", "unknown72", "unknown80", "unknown88",
2186  "unknown96", "unknown104", "unknown112", "unknown120",
2187  "unknown128" };
2188  return s[(sizeof(T)<17)?sizeof(T):0];
2189  }
2190  static bool is_float() { return false; }
2191  static bool is_inf(const T) { return false; }
2192  static bool is_nan(const T) { return false; }
2193  static T min() { return (T)-1>0?(T)0:(T)-1<<(8*sizeof(T)-1); }
2194  static T max() { return (T)-1>0?(T)-1:~((T)-1<<(8*sizeof(T)-1)); }
2195  static T inf() { return max(); }
2196  static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
2197  static const char* format() { return "%s"; }
2198  static const char* format(const T val) { static const char *const s = "unknown"; cimg::unused(val); return s; }
2199  };
2200 
2201  template<> struct type<bool> {
2202  static const char* string() { static const char *const s = "bool"; return s; }
2203  static bool is_float() { return false; }
2204  static bool is_inf(const bool) { return false; }
2205  static bool is_nan(const bool) { return false; }
2206  static bool min() { return false; }
2207  static bool max() { return true; }
2208  static bool inf() { return max(); }
2209  static bool is_inf() { return false; }
2210  static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
2211  static const char* format() { return "%s"; }
2212  static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
2213  };
2214 
2215  template<> struct type<unsigned char> {
2216  static const char* string() { static const char *const s = "unsigned char"; return s; }
2217  static bool is_float() { return false; }
2218  static bool is_inf(const unsigned char) { return false; }
2219  static bool is_nan(const unsigned char) { return false; }
2220  static unsigned char min() { return 0; }
2221  static unsigned char max() { return (unsigned char)~0U; }
2222  static unsigned char inf() { return max(); }
2223  static unsigned char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
2224  static const char* format() { return "%u"; }
2225  static unsigned int format(const unsigned char val) { return (unsigned int)val; }
2226  };
2227 
2228  template<> struct type<char> {
2229  static const char* string() { static const char *const s = "char"; return s; }
2230  static bool is_float() { return false; }
2231  static bool is_inf(const char) { return false; }
2232  static bool is_nan(const char) { return false; }
2233  static char min() { return (char)(-1L<<(8*sizeof(char)-1)); }
2234  static char max() { return (char)~((char)(-1L<<(8*sizeof(char)-1))); }
2235  static char inf() { return max(); }
2236  static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
2237  static const char* format() { return "%d"; }
2238  static int format(const char val) { return (int)val; }
2239  };
2240 
2241  template<> struct type<signed char> {
2242  static const char* string() { static const char *const s = "signed char"; return s; }
2243  static bool is_float() { return false; }
2244  static bool is_inf(const signed char) { return false; }
2245  static bool is_nan(const signed char) { return false; }
2246  static signed char min() { return (signed char)(-1L<<(8*sizeof(signed char)-1)); }
2247  static signed char max() { return ~((signed char)(-1L<<(8*sizeof(signed char)-1))); }
2248  static signed char inf() { return max(); }
2249  static signed char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
2250  static const char* format() { return "%d"; }
2251  static unsigned int format(const signed char val) { return (int)val; }
2252  };
2253 
2254  template<> struct type<unsigned short> {
2255  static const char* string() { static const char *const s = "unsigned short"; return s; }
2256  static bool is_float() { return false; }
2257  static bool is_inf(const unsigned short) { return false; }
2258  static bool is_nan(const unsigned short) { return false; }
2259  static unsigned short min() { return 0; }
2260  static unsigned short max() { return (unsigned short)~0U; }
2261  static unsigned short inf() { return max(); }
2262  static unsigned short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
2263  static const char* format() { return "%u"; }
2264  static unsigned int format(const unsigned short val) { return (unsigned int)val; }
2265  };
2266 
2267  template<> struct type<short> {
2268  static const char* string() { static const char *const s = "short"; return s; }
2269  static bool is_float() { return false; }
2270  static bool is_inf(const short) { return false; }
2271  static bool is_nan(const short) { return false; }
2272  static short min() { return (short)(-1L<<(8*sizeof(short)-1)); }
2273  static short max() { return ~((short)(-1L<<(8*sizeof(short)-1))); }
2274  static short inf() { return max(); }
2275  static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
2276  static const char* format() { return "%d"; }
2277  static int format(const short val) { return (int)val; }
2278  };
2279 
2280  template<> struct type<unsigned int> {
2281  static const char* string() { static const char *const s = "unsigned int"; return s; }
2282  static bool is_float() { return false; }
2283  static bool is_inf(const unsigned int) { return false; }
2284  static bool is_nan(const unsigned int) { return false; }
2285  static unsigned int min() { return 0; }
2286  static unsigned int max() { return (unsigned int)~0U; }
2287  static unsigned int inf() { return max(); }
2288  static unsigned int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
2289  static const char* format() { return "%u"; }
2290  static unsigned int format(const unsigned int val) { return val; }
2291  };
2292 
2293  template<> struct type<int> {
2294  static const char* string() { static const char *const s = "int"; return s; }
2295  static bool is_float() { return false; }
2296  static bool is_inf(const int) { return false; }
2297  static bool is_nan(const int) { return false; }
2298  static int min() { return (int)(-1L<<(8*sizeof(int)-1)); }
2299  static int max() { return ~((int)(-1L<<(8*sizeof(int)-1))); }
2300  static int inf() { return max(); }
2301  static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
2302  static const char* format() { return "%d"; }
2303  static int format(const int val) { return val; }
2304  };
2305 
2306  template<> struct type<unsigned long> {
2307  static const char* string() { static const char *const s = "unsigned long"; return s; }
2308  static bool is_float() { return false; }
2309  static bool is_inf(const unsigned long) { return false; }
2310  static bool is_nan(const unsigned long) { return false; }
2311  static unsigned long min() { return 0; }
2312  static unsigned long max() { return (unsigned long)~0UL; }
2313  static unsigned long inf() { return max(); }
2314  static unsigned long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(unsigned long)val; }
2315  static const char* format() { return "%lu"; }
2316  static unsigned long format(const unsigned long val) { return val; }
2317  };
2318 
2319  template<> struct type<long> {
2320  static const char* string() { static const char *const s = "long"; return s; }
2321  static bool is_float() { return false; }
2322  static bool is_inf(const long) { return false; }
2323  static bool is_nan(const long) { return false; }
2324  static long min() { return (long)(-1L<<(8*sizeof(long)-1)); }
2325  static long max() { return ~((long)(-1L<<(8*sizeof(long)-1))); }
2326  static long inf() { return max(); }
2327  static long cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(long)val; }
2328  static const char* format() { return "%ld"; }
2329  static long format(const long val) { return val; }
2330  };
2331 
2332  template<> struct type<double> {
2333  static const char* string() { static const char *const s = "double"; return s; }
2334  static bool is_float() { return true; }
2335  static bool is_inf(const double val) { return !is_nan(val) && val+1==val; }
2336  static bool is_nan(const double val) { return !(val<=0 || val>=0); }
2337  static double min() { return -1.7E308; }
2338  static double max() { return 1.7E308; }
2339  static double inf() { return max()*max(); }
2340  static double nan() { static const double val_nan = -std::sqrt(-1.0); return val_nan; }
2341  static double cut(const double val) { return val<min()?min():val>max()?max():val; }
2342  static const char* format() { return "%.16g"; }
2343  static double format(const double val) { return val; }
2344  };
2345 
2346  template<> struct type<float> {
2347  static const char* string() { static const char *const s = "float"; return s; }
2348  static bool is_float() { return true; }
2349  static bool is_inf(const float val) { return cimg::type<double>::is_inf((double)val); }
2350  static bool is_nan(const float val) { return cimg::type<double>::is_nan((double)val); }
2351  static float min() { return -3.4E38f; }
2352  static float max() { return 3.4E38f; }
2353  static float inf() { return (float)cimg::type<double>::inf(); }
2354  static float nan() { return (float)cimg::type<double>::nan(); }
2355  static float cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(float)val; }
2356  static const char* format() { return "%.16g"; }
2357  static double format(const float val) { return (double)val; }
2358  };
2359 
2360  template<typename T, typename t> struct superset { typedef T type; };
2361  template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
2362  template<> struct superset<bool,char> { typedef char type; };
2363  template<> struct superset<bool,signed char> { typedef signed char type; };
2364  template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
2365  template<> struct superset<bool,short> { typedef short type; };
2366  template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
2367  template<> struct superset<bool,int> { typedef int type; };
2368  template<> struct superset<bool,unsigned long> { typedef unsigned long type; };
2369  template<> struct superset<bool,long> { typedef long type; };
2370  template<> struct superset<bool,float> { typedef float type; };
2371  template<> struct superset<bool,double> { typedef double type; };
2372  template<> struct superset<unsigned char,char> { typedef short type; };
2373  template<> struct superset<unsigned char,signed char> { typedef short type; };
2374  template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
2375  template<> struct superset<unsigned char,short> { typedef short type; };
2376  template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
2377  template<> struct superset<unsigned char,int> { typedef int type; };
2378  template<> struct superset<unsigned char,unsigned long> { typedef unsigned long type; };
2379  template<> struct superset<unsigned char,long> { typedef long type; };
2380  template<> struct superset<unsigned char,float> { typedef float type; };
2381  template<> struct superset<unsigned char,double> { typedef double type; };
2382  template<> struct superset<signed char,unsigned char> { typedef short type; };
2383  template<> struct superset<signed char,char> { typedef short type; };
2384  template<> struct superset<signed char,unsigned short> { typedef int type; };
2385  template<> struct superset<signed char,short> { typedef short type; };
2386  template<> struct superset<signed char,unsigned int> { typedef long type; };
2387  template<> struct superset<signed char,int> { typedef int type; };
2388  template<> struct superset<signed char,unsigned long> { typedef long type; };
2389  template<> struct superset<signed char,long> { typedef long type; };
2390  template<> struct superset<signed char,float> { typedef float type; };
2391  template<> struct superset<signed char,double> { typedef double type; };
2392  template<> struct superset<char,unsigned char> { typedef short type; };
2393  template<> struct superset<char,signed char> { typedef short type; };
2394  template<> struct superset<char,unsigned short> { typedef int type; };
2395  template<> struct superset<char,short> { typedef short type; };
2396  template<> struct superset<char,unsigned int> { typedef long type; };
2397  template<> struct superset<char,int> { typedef int type; };
2398  template<> struct superset<char,unsigned long> { typedef long type; };
2399  template<> struct superset<char,long> { typedef long type; };
2400  template<> struct superset<char,float> { typedef float type; };
2401  template<> struct superset<char,double> { typedef double type; };
2402  template<> struct superset<unsigned short,char> { typedef int type; };
2403  template<> struct superset<unsigned short,signed char> { typedef int type; };
2404  template<> struct superset<unsigned short,short> { typedef int type; };
2405  template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
2406  template<> struct superset<unsigned short,int> { typedef int type; };
2407  template<> struct superset<unsigned short,unsigned long> { typedef unsigned long type; };
2408  template<> struct superset<unsigned short,long> { typedef long type; };
2409  template<> struct superset<unsigned short,float> { typedef float type; };
2410  template<> struct superset<unsigned short,double> { typedef double type; };
2411  template<> struct superset<short,unsigned short> { typedef int type; };
2412  template<> struct superset<short,unsigned int> { typedef long type; };
2413  template<> struct superset<short,int> { typedef int type; };
2414  template<> struct superset<short,unsigned long> { typedef long type; };
2415  template<> struct superset<short,long> { typedef long type; };
2416  template<> struct superset<short,float> { typedef float type; };
2417  template<> struct superset<short,double> { typedef double type; };
2418  template<> struct superset<unsigned int,char> { typedef long type; };
2419  template<> struct superset<unsigned int,signed char> { typedef long type; };
2420  template<> struct superset<unsigned int,short> { typedef long type; };
2421  template<> struct superset<unsigned int,int> { typedef long type; };
2422  template<> struct superset<unsigned int,unsigned long> { typedef unsigned long type; };
2423  template<> struct superset<unsigned int,long> { typedef long type; };
2424  template<> struct superset<unsigned int,float> { typedef float type; };
2425  template<> struct superset<unsigned int,double> { typedef double type; };
2426  template<> struct superset<int,unsigned int> { typedef long type; };
2427  template<> struct superset<int,unsigned long> { typedef long type; };
2428  template<> struct superset<int,long> { typedef long type; };
2429  template<> struct superset<int,float> { typedef float type; };
2430  template<> struct superset<int,double> { typedef double type; };
2431  template<> struct superset<unsigned long,char> { typedef long type; };
2432  template<> struct superset<unsigned long,signed char> { typedef long type; };
2433  template<> struct superset<unsigned long,short> { typedef long type; };
2434  template<> struct superset<unsigned long,int> { typedef long type; };
2435  template<> struct superset<unsigned long,long> { typedef long type; };
2436  template<> struct superset<unsigned long,float> { typedef float type; };
2437  template<> struct superset<unsigned long,double> { typedef double type; };
2438  template<> struct superset<long,float> { typedef float type; };
2439  template<> struct superset<long,double> { typedef double type; };
2440  template<> struct superset<float,double> { typedef double type; };
2441 
2442  template<typename t1, typename t2, typename t3> struct superset2 {
2443  typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
2444  };
2445 
2446  template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
2447  typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
2448  };
2449 
2450  template<typename t1, typename t2> struct last { typedef t2 type; };
2451 
2452 #define _cimg_Tt typename cimg::superset<T,t>::type
2453 #define _cimg_Tfloat typename cimg::superset<T,float>::type
2454 #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
2455 #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
2456 
2457  // Define variables used internally by CImg.
2458 #if cimg_display==1
2459  struct X11_info {
2460  volatile unsigned int nb_wins;
2461  pthread_t* event_thread;
2462  CImgDisplay* wins[1024];
2463  Display* display;
2464  unsigned int nb_bits;
2465  bool is_blue_first;
2466  bool is_shm_enabled;
2467  bool byte_order;
2468 #ifdef cimg_use_xrandr
2469  XRRScreenSize *resolutions;
2470  Rotation curr_rotation;
2471  unsigned int curr_resolution;
2472  unsigned int nb_resolutions;
2473 #endif
2474  X11_info():nb_wins(0),event_thread(0),display(0),
2475  nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
2476 #ifdef cimg_use_xrandr
2477  resolutions = 0;
2478  curr_rotation = 0;
2479  curr_resolution = nb_resolutions = 0;
2480 #endif
2481  }
2482  };
2483 #if defined(cimg_module)
2484  X11_info& X11_attr();
2485 #elif defined(cimg_main)
2486  X11_info& X11_attr() { static X11_info val; return val; }
2487 #else
2488  inline X11_info& X11_attr() { static X11_info val; return val; }
2489 #endif
2490 
2491 #elif cimg_display==2
2492  struct Win32_info {
2493  HANDLE wait_event;
2494  Win32_info() { wait_event = CreateEvent(0,FALSE,FALSE,0); }
2495  };
2496 #if defined(cimg_module)
2497  Win32_info& Win32_attr();
2498 #elif defined(cimg_main)
2499  Win32_info& Win32_attr() { static Win32_info val; return val; }
2500 #else
2501  inline Win32_info& Win32_attr() { static Win32_info val; return val; }
2502 #endif
2503 #endif
2504 
2505 #if defined(cimg_use_magick)
2506  static struct Magick_info {
2507  Magick_info() {
2508  Magick::InitializeMagick("");
2509  }
2510  } _Magick_info;
2511 #endif
2512 
2513 #if cimg_display==1
2514  // Define keycodes for X11-based graphical systems.
2515  const unsigned int keyESC = XK_Escape;
2516  const unsigned int keyF1 = XK_F1;
2517  const unsigned int keyF2 = XK_F2;
2518  const unsigned int keyF3 = XK_F3;
2519  const unsigned int keyF4 = XK_F4;
2520  const unsigned int keyF5 = XK_F5;
2521  const unsigned int keyF6 = XK_F6;
2522  const unsigned int keyF7 = XK_F7;
2523  const unsigned int keyF8 = XK_F8;
2524  const unsigned int keyF9 = XK_F9;
2525  const unsigned int keyF10 = XK_F10;
2526  const unsigned int keyF11 = XK_F11;
2527  const unsigned int keyF12 = XK_F12;
2528  const unsigned int keyPAUSE = XK_Pause;
2529  const unsigned int key1 = XK_1;
2530  const unsigned int key2 = XK_2;
2531  const unsigned int key3 = XK_3;
2532  const unsigned int key4 = XK_4;
2533  const unsigned int key5 = XK_5;
2534  const unsigned int key6 = XK_6;
2535  const unsigned int key7 = XK_7;
2536  const unsigned int key8 = XK_8;
2537  const unsigned int key9 = XK_9;
2538  const unsigned int key0 = XK_0;
2539  const unsigned int keyBACKSPACE = XK_BackSpace;
2540  const unsigned int keyINSERT = XK_Insert;
2541  const unsigned int keyHOME = XK_Home;
2542  const unsigned int keyPAGEUP = XK_Page_Up;
2543  const unsigned int keyTAB = XK_Tab;
2544  const unsigned int keyQ = XK_q;
2545  const unsigned int keyW = XK_w;
2546  const unsigned int keyE = XK_e;
2547  const unsigned int keyR = XK_r;
2548  const unsigned int keyT = XK_t;
2549  const unsigned int keyY = XK_y;
2550  const unsigned int keyU = XK_u;
2551  const unsigned int keyI = XK_i;
2552  const unsigned int keyO = XK_o;
2553  const unsigned int keyP = XK_p;
2554  const unsigned int keyDELETE = XK_Delete;
2555  const unsigned int keyEND = XK_End;
2556  const unsigned int keyPAGEDOWN = XK_Page_Down;
2557  const unsigned int keyCAPSLOCK = XK_Caps_Lock;
2558  const unsigned int keyA = XK_a;
2559  const unsigned int keyS = XK_s;
2560  const unsigned int keyD = XK_d;
2561  const unsigned int keyF = XK_f;
2562  const unsigned int keyG = XK_g;
2563  const unsigned int keyH = XK_h;
2564  const unsigned int keyJ = XK_j;
2565  const unsigned int keyK = XK_k;
2566  const unsigned int keyL = XK_l;
2567  const unsigned int keyENTER = XK_Return;
2568  const unsigned int keySHIFTLEFT = XK_Shift_L;
2569  const unsigned int keyZ = XK_z;
2570  const unsigned int keyX = XK_x;
2571  const unsigned int keyC = XK_c;
2572  const unsigned int keyV = XK_v;
2573  const unsigned int keyB = XK_b;
2574  const unsigned int keyN = XK_n;
2575  const unsigned int keyM = XK_m;
2576  const unsigned int keySHIFTRIGHT = XK_Shift_R;
2577  const unsigned int keyARROWUP = XK_Up;
2578  const unsigned int keyCTRLLEFT = XK_Control_L;
2579  const unsigned int keyAPPLEFT = XK_Super_L;
2580  const unsigned int keyALT = XK_Alt_L;
2581  const unsigned int keySPACE = XK_space;
2582  const unsigned int keyALTGR = XK_Alt_R;
2583  const unsigned int keyAPPRIGHT = XK_Super_R;
2584  const unsigned int keyMENU = XK_Menu;
2585  const unsigned int keyCTRLRIGHT = XK_Control_R;
2586  const unsigned int keyARROWLEFT = XK_Left;
2587  const unsigned int keyARROWDOWN = XK_Down;
2588  const unsigned int keyARROWRIGHT = XK_Right;
2589  const unsigned int keyPAD0 = XK_KP_0;
2590  const unsigned int keyPAD1 = XK_KP_1;
2591  const unsigned int keyPAD2 = XK_KP_2;
2592  const unsigned int keyPAD3 = XK_KP_3;
2593  const unsigned int keyPAD4 = XK_KP_4;
2594  const unsigned int keyPAD5 = XK_KP_5;
2595  const unsigned int keyPAD6 = XK_KP_6;
2596  const unsigned int keyPAD7 = XK_KP_7;
2597  const unsigned int keyPAD8 = XK_KP_8;
2598  const unsigned int keyPAD9 = XK_KP_9;
2599  const unsigned int keyPADADD = XK_KP_Add;
2600  const unsigned int keyPADSUB = XK_KP_Subtract;
2601  const unsigned int keyPADMUL = XK_KP_Multiply;
2602  const unsigned int keyPADDIV = XK_KP_Divide;
2603 
2604 #elif cimg_display==2
2605  // Define keycodes for Windows.
2606  const unsigned int keyESC = VK_ESCAPE;
2607  const unsigned int keyF1 = VK_F1;
2608  const unsigned int keyF2 = VK_F2;
2609  const unsigned int keyF3 = VK_F3;
2610  const unsigned int keyF4 = VK_F4;
2611  const unsigned int keyF5 = VK_F5;
2612  const unsigned int keyF6 = VK_F6;
2613  const unsigned int keyF7 = VK_F7;
2614  const unsigned int keyF8 = VK_F8;
2615  const unsigned int keyF9 = VK_F9;
2616  const unsigned int keyF10 = VK_F10;
2617  const unsigned int keyF11 = VK_F11;
2618  const unsigned int keyF12 = VK_F12;
2619  const unsigned int keyPAUSE = VK_PAUSE;
2620  const unsigned int key1 = '1';
2621  const unsigned int key2 = '2';
2622  const unsigned int key3 = '3';
2623  const unsigned int key4 = '4';
2624  const unsigned int key5 = '5';
2625  const unsigned int key6 = '6';
2626  const unsigned int key7 = '7';
2627  const unsigned int key8 = '8';
2628  const unsigned int key9 = '9';
2629  const unsigned int key0 = '0';
2630  const unsigned int keyBACKSPACE = VK_BACK;
2631  const unsigned int keyINSERT = VK_INSERT;
2632  const unsigned int keyHOME = VK_HOME;
2633  const unsigned int keyPAGEUP = VK_PRIOR;
2634  const unsigned int keyTAB = VK_TAB;
2635  const unsigned int keyQ = 'Q';
2636  const unsigned int keyW = 'W';
2637  const unsigned int keyE = 'E';
2638  const unsigned int keyR = 'R';
2639  const unsigned int keyT = 'T';
2640  const unsigned int keyY = 'Y';
2641  const unsigned int keyU = 'U';
2642  const unsigned int keyI = 'I';
2643  const unsigned int keyO = 'O';
2644  const unsigned int keyP = 'P';
2645  const unsigned int keyDELETE = VK_DELETE;
2646  const unsigned int keyEND = VK_END;
2647  const unsigned int keyPAGEDOWN = VK_NEXT;
2648  const unsigned int keyCAPSLOCK = VK_CAPITAL;
2649  const unsigned int keyA = 'A';
2650  const unsigned int keyS = 'S';
2651  const unsigned int keyD = 'D';
2652  const unsigned int keyF = 'F';
2653  const unsigned int keyG = 'G';
2654  const unsigned int keyH = 'H';
2655  const unsigned int keyJ = 'J';
2656  const unsigned int keyK = 'K';
2657  const unsigned int keyL = 'L';
2658  const unsigned int keyENTER = VK_RETURN;
2659  const unsigned int keySHIFTLEFT = VK_SHIFT;
2660  const unsigned int keyZ = 'Z';
2661  const unsigned int keyX = 'X';
2662  const unsigned int keyC = 'C';
2663  const unsigned int keyV = 'V';
2664  const unsigned int keyB = 'B';
2665  const unsigned int keyN = 'N';
2666  const unsigned int keyM = 'M';
2667  const unsigned int keySHIFTRIGHT = VK_SHIFT;
2668  const unsigned int keyARROWUP = VK_UP;
2669  const unsigned int keyCTRLLEFT = VK_CONTROL;
2670  const unsigned int keyAPPLEFT = VK_LWIN;
2671  const unsigned int keyALT = VK_LMENU;
2672  const unsigned int keySPACE = VK_SPACE;
2673  const unsigned int keyALTGR = VK_CONTROL;
2674  const unsigned int keyAPPRIGHT = VK_RWIN;
2675  const unsigned int keyMENU = VK_APPS;
2676  const unsigned int keyCTRLRIGHT = VK_CONTROL;
2677  const unsigned int keyARROWLEFT = VK_LEFT;
2678  const unsigned int keyARROWDOWN = VK_DOWN;
2679  const unsigned int keyARROWRIGHT = VK_RIGHT;
2680  const unsigned int keyPAD0 = 0x60;
2681  const unsigned int keyPAD1 = 0x61;
2682  const unsigned int keyPAD2 = 0x62;
2683  const unsigned int keyPAD3 = 0x63;
2684  const unsigned int keyPAD4 = 0x64;
2685  const unsigned int keyPAD5 = 0x65;
2686  const unsigned int keyPAD6 = 0x66;
2687  const unsigned int keyPAD7 = 0x67;
2688  const unsigned int keyPAD8 = 0x68;
2689  const unsigned int keyPAD9 = 0x69;
2690  const unsigned int keyPADADD = VK_ADD;
2691  const unsigned int keyPADSUB = VK_SUBTRACT;
2692  const unsigned int keyPADMUL = VK_MULTIPLY;
2693  const unsigned int keyPADDIV = VK_DIVIDE;
2694 
2695 #else
2696  // Define random keycodes when no display is available.
2697  // (should rarely be used then!).
2698  const unsigned int keyESC = 1U;
2699  const unsigned int keyF1 = 2U;
2700  const unsigned int keyF2 = 3U;
2701  const unsigned int keyF3 = 4U;
2702  const unsigned int keyF4 = 5U;
2703  const unsigned int keyF5 = 6U;
2704  const unsigned int keyF6 = 7U;
2705  const unsigned int keyF7 = 8U;
2706  const unsigned int keyF8 = 9U;
2707  const unsigned int keyF9 = 10U;
2708  const unsigned int keyF10 = 11U;
2709  const unsigned int keyF11 = 12U;
2710  const unsigned int keyF12 = 13U;
2711  const unsigned int keyPAUSE = 14U;
2712  const unsigned int key1 = 15U;
2713  const unsigned int key2 = 16U;
2714  const unsigned int key3 = 17U;
2715  const unsigned int key4 = 18U;
2716  const unsigned int key5 = 19U;
2717  const unsigned int key6 = 20U;
2718  const unsigned int key7 = 21U;
2719  const unsigned int key8 = 22U;
2720  const unsigned int key9 = 23U;
2721  const unsigned int key0 = 24U;
2722  const unsigned int keyBACKSPACE = 25U;
2723  const unsigned int keyINSERT = 26U;
2724  const unsigned int keyHOME = 27U;
2725  const unsigned int keyPAGEUP = 28U;
2726  const unsigned int keyTAB = 29U;
2727  const unsigned int keyQ = 30U;
2728  const unsigned int keyW = 31U;
2729  const unsigned int keyE = 32U;
2730  const unsigned int keyR = 33U;
2731  const unsigned int keyT = 34U;
2732  const unsigned int keyY = 35U;
2733  const unsigned int keyU = 36U;
2734  const unsigned int keyI = 37U;
2735  const unsigned int keyO = 38U;
2736  const unsigned int keyP = 39U;
2737  const unsigned int keyDELETE = 40U;
2738  const unsigned int keyEND = 41U;
2739  const unsigned int keyPAGEDOWN = 42U;
2740  const unsigned int keyCAPSLOCK = 43U;
2741  const unsigned int keyA = 44U;
2742  const unsigned int keyS = 45U;
2743  const unsigned int keyD = 46U;
2744  const unsigned int keyF = 47U;
2745  const unsigned int keyG = 48U;
2746  const unsigned int keyH = 49U;
2747  const unsigned int keyJ = 50U;
2748  const unsigned int keyK = 51U;
2749  const unsigned int keyL = 52U;
2750  const unsigned int keyENTER = 53U;
2751  const unsigned int keySHIFTLEFT = 54U;
2752  const unsigned int keyZ = 55U;
2753  const unsigned int keyX = 56U;
2754  const unsigned int keyC = 57U;
2755  const unsigned int keyV = 58U;
2756  const unsigned int keyB = 59U;
2757  const unsigned int keyN = 60U;
2758  const unsigned int keyM = 61U;
2759  const unsigned int keySHIFTRIGHT = 62U;
2760  const unsigned int keyARROWUP = 63U;
2761  const unsigned int keyCTRLLEFT = 64U;
2762  const unsigned int keyAPPLEFT = 65U;
2763  const unsigned int keyALT = 66U;
2764  const unsigned int keySPACE = 67U;
2765  const unsigned int keyALTGR = 68U;
2766  const unsigned int keyAPPRIGHT = 69U;
2767  const unsigned int keyMENU = 70U;
2768  const unsigned int keyCTRLRIGHT = 71U;
2769  const unsigned int keyARROWLEFT = 72U;
2770  const unsigned int keyARROWDOWN = 73U;
2771  const unsigned int keyARROWRIGHT = 74U;
2772  const unsigned int keyPAD0 = 75U;
2773  const unsigned int keyPAD1 = 76U;
2774  const unsigned int keyPAD2 = 77U;
2775  const unsigned int keyPAD3 = 78U;
2776  const unsigned int keyPAD4 = 79U;
2777  const unsigned int keyPAD5 = 80U;
2778  const unsigned int keyPAD6 = 81U;
2779  const unsigned int keyPAD7 = 82U;
2780  const unsigned int keyPAD8 = 83U;
2781  const unsigned int keyPAD9 = 84U;
2782  const unsigned int keyPADADD = 85U;
2783  const unsigned int keyPADSUB = 86U;
2784  const unsigned int keyPADMUL = 87U;
2785  const unsigned int keyPADDIV = 88U;
2786 #endif
2787 
2788  const double PI = 3.14159265358979323846;
2789 
2790  // Define a 10x13 font (small size).
2791  const unsigned int font10x13[256*10*13/32] = {
2792  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2793  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80100c0,
2794  0x68000300,0x801,0xc00010,0x100c000,0x68100,0x100c0680,0x2,0x403000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2795  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2796  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x4020120,
2797  0x58120480,0x402,0x1205008,0x2012050,0x58080,0x20120581,0x40000001,0x804812,0x2000000,0x0,0x300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2798  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x140,0x80000,0x200402,0x800000,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2799  0x0,0x7010,0x7000000,0x8000200,0x20000,0xc0002000,0x8008,0x0,0x0,0x0,0x0,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2800  0x0,0x0,0x80000000,0x0,0x0,0x0,0x40000,0x0,0x0,0x0,0x0,0x480,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x80100c0,0x68000480,0x1001,
2801  0xc00010,0x1018000,0x68100,0x100c0680,0x4,0x403000,0x1020000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,0x28081883,0x200801,
2802  0x2a00000,0x10,0x1c0201c0,0x70040f80,0xc0f81c07,0x0,0x70,0x3e0303c0,0x3c3c0f83,0xe03c2107,0xe08810,0x18c31070,0x3c0703c0,
2803  0x783e0842,0x22222208,0x83e04010,0x1008000,0x4000200,0x20001,0x2002,0x408008,0x0,0x0,0x100000,0x0,0x1008,0x2000000,0x0,0x0,0x0,
2804  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20080,0x38000880,0x8078140f,0x81c00000,0x3e000,0xc020180,0x60080001,0xe0000002,0xc00042,0x108e2010,
2805  0xc0300c0,0x300c0303,0xf83c3e0f,0x83e0f81c,0x701c070,0x3c0c41c0,0x701c0701,0xc0001d08,0x42108421,0x8820088,0x4020120,0x58140480,
2806  0x802,0x1205008,0x3014050,0xc058080,0x20120581,0x40000002,0x804814,0x2020050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20140,
2807  0x281e2484,0x80200801,0x1c02000,0x10,0x22060220,0x880c0801,0x82208,0x80000001,0x20008,0x41030220,0x40220802,0x402102,0x209010,
2808  0x18c31088,0x22088220,0x80080842,0x22222208,0x80204010,0x1014000,0x200,0x20001,0x2000,0x8008,0x0,0x0,0x100000,0x0,0x1008,
2809  0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x40000500,0x80800010,0x40200000,0x41000,0x12020040,0x10000003,0xa0000006,
2810  0x12000c4,0x31014000,0xc0300c0,0x300c0302,0x80402008,0x2008008,0x2008020,0x220c4220,0x88220882,0x20002208,0x42108421,0x8820088,
2811  0x0,0x300,0x0,0x0,0x0,0x14000000,0x0,0x200200,0x0,0x20000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xfc282504,0x80001000,
2812  0x82a02000,0x20,0x22020020,0x8140802,0x102208,0x80801006,0x18008,0x9c848220,0x80210802,0x802102,0x20a010,0x15429104,0x22104220,
2813  0x80080842,0x22221405,0x404008,0x1022000,0x703c0,0x381e0701,0xc0783c02,0xc09008,0x1d83c070,0x3c078140,0x381c0882,0x21242208,
2814  0x81e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,0x40220500,0x80800027,0x20e02800,0x9c800,0x12020040,
2815  0x20000883,0xa0200002,0x120a044,0x11064010,0x12048120,0x48120484,0x80802008,0x2008008,0x2008020,0x210a4411,0x4411044,0x10884508,
2816  0x42108421,0x503c0b0,0x1c0701c0,0x701c0707,0x70381c07,0x1c07008,0x2008020,0x20f01c0,0x701c0701,0xc0201c08,0x82208822,0x883c088,
2817  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x50281903,0x20001000,0x80802000,0x20,0x22020040,0x30240f03,0xc0101c08,0x80801018,
2818  0x1fc06010,0xa48483c0,0x80210f03,0xe0803f02,0x20c010,0x15429104,0x22104220,0x70080841,0x41540805,0x804008,0x1041000,0x8220,
2819  0x40220881,0x882202,0x40a008,0x12422088,0x22088180,0x40100882,0x21241408,0x80201008,0x2031000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2820  0x0,0x20280,0x401c0200,0x700028,0x21205000,0x92800,0xc1fc080,0x10000883,0xa0200002,0x1205049,0x12c19010,0x12048120,0x48120484,
2821  0xf0803c0f,0x3c0f008,0x2008020,0x790a4411,0x4411044,0x10504908,0x42108421,0x5022088,0x2008020,0x8020080,0x88402208,0x82208808,
2822  0x2008020,0x1e088220,0x88220882,0x20002608,0x82208822,0x8822088,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0x501c0264,
2823  0xa0001000,0x8001fc00,0x7000020,0x22020080,0x83e0082,0x20202207,0x80000020,0x1020,0xa4848220,0x80210802,0x9c2102,0x20c010,
2824  0x12425104,0x3c1043c0,0x8080841,0x41540802,0x804008,0x1000000,0x78220,0x40220f81,0x882202,0x40c008,0x12422088,0x22088100,
2825  0x60100881,0x41540805,0x406008,0x1849000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0xf0140200,0x880028,0x20e0a03f,0x709c800,
2826  0x201c0,0x60000881,0xa0000007,0xc0284b,0x122eb020,0x12048120,0x48120487,0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,
2827  0x10204908,0x42108421,0x2022088,0x1e0781e0,0x781e0787,0xf8403e0f,0x83e0f808,0x2008020,0x22088220,0x88220882,0x21fc2a08,0x82208822,
2828  0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20001,0xf80a0294,0x40001000,0x80002000,0x20,0x22020100,0x8040082,0x20202200,
2829  0x80000018,0x1fc06020,0xa48fc220,0x80210802,0x842102,0x20a010,0x12425104,0x20104240,0x8080841,0x41541402,0x1004008,0x1000000,
2830  0x88220,0x40220801,0x882202,0x40a008,0x12422088,0x22088100,0x18100881,0x41540805,0x801008,0x2046000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2831  0x0,0x0,0x0,0x20280,0x401c0f80,0x80880028,0x20005001,0x94800,0x20000,0x880,0xa0000000,0x5015,0x4215040,0x3f0fc3f0,0xfc3f0fc8,
2832  0x80802008,0x2008008,0x2008020,0x21094411,0x4411044,0x10505108,0x42108421,0x203c088,0x22088220,0x88220888,0x80402008,0x2008008,
2833  0x2008020,0x22088220,0x88220882,0x20002a08,0x82208822,0x5022050,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xa00a0494,0x60001000,
2834  0x80002004,0x8020,0x22020200,0x88040882,0x20402201,0x801006,0x18000,0x9f084220,0x40220802,0x442102,0x209010,0x10423088,0x20088220,
2835  0x8080840,0x80882202,0x2004008,0x1000000,0x88220,0x40220881,0x882202,0x409008,0x12422088,0x22088100,0x8100880,0x80881402,
2836  0x1001008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20280,0x40220200,0x80700027,0x20002801,0x92800,0x1fc000,0x980,
2837  0xa0000000,0xa017,0x84417840,0x21084210,0x84210848,0x80402008,0x2008008,0x2008020,0x2208c220,0x88220882,0x20882208,0x42108421,
2838  0x2020088,0x22088220,0x88220888,0xc8402208,0x82208808,0x2008020,0x22088220,0x88220882,0x20203208,0x82208822,0x2022020,0x0,0x0,0x0,
2839  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000,0xa03c0463,0x90000801,0x2004,0x8040,0x1c0703e0,0x70040701,0xc0401c06,0x801001,0x20020,
2840  0x400843c0,0x3c3c0f82,0x3c2107,0x1c0881e,0x10423070,0x20070210,0xf0080780,0x80882202,0x3e04004,0x1000000,0x783c0,0x381e0701,
2841  0x782202,0x408808,0x12422070,0x3c078100,0x700c0780,0x80882202,0x1e01008,0x2000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x201e0,
2842  0xf8000200,0x80080010,0x40000001,0x41000,0x0,0xe80,0xa0000000,0x21,0x8e21038,0x21084210,0x84210848,0xf83c3e0f,0x83e0f81c,
2843  0x701c070,0x3c08c1c0,0x701c0701,0xc0005c07,0x81e0781e,0x20200b0,0x1e0781e0,0x781e0787,0x30381c07,0x1c07008,0x2008020,0x1c0881c0,
2844  0x701c0701,0xc0201c07,0x81e0781e,0x203c020,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,0x801,0x4,0x40,0x0,0x0,0x0,0x1000,
2845  0x0,0x3c000000,0x0,0x0,0x0,0x0,0x10000,0x0,0x0,0x4004,0x1000000,0x0,0x0,0x80000,0x400000,0x0,0x20008000,0x0,0x4,0x1008,0x2000000,
2846  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x8008000f,0x80000000,0x3e000,0x0,0x800,0xa0000400,0x0,0x0,0x0,0x0,0x80000,0x0,
2847  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100000,0x0,0x0,0x0,0x0,0x2000,0x0,0x4020040,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80000,
2848  0x402,0x8,0x40,0x0,0x0,0x0,0x2000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x7004,0x70000fc,0x0,0x0,0x700000,0x800000,0x0,0x20008000,
2849  0x0,0x4,0x808,0x4000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x80,0x0,0x80f00000,0x0,0x0,0x0,0x800,0xa0001800,0x0,0x0,0x0,0x0,
2850  0x300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x4020040
2851  };
2852 
2853  // Define a 12x24 font (normal size).
2854  const unsigned int font12x24[12*24*256/32] = {
2855  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2856  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x19,0x80000000,0x198000,0x0,0x0,0x0,0x0,
2857  0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc001806,0xc81980,0x60000000,0xc001806,0x1980c00,0x18060198,0xc80c,
2858  0x180600,0xc8198000,0xc001,0x80601980,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2859  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2860  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x198,0x0,0x0,0x0,0x0,0x0,0x0,
2861  0x0,0x0,0x0,0x0,0x0,0x0,0x600300f,0x1301980,0x90000000,0x600300f,0x1980600,0x300f0198,0x13006,0x300f01,0x30198000,0x6003,
2862  0xf01980,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2863  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2864  0x0,0x0,0x0,0x0,0x0,0x0,0x6,0x0,0x60000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7007,0x3c0000,0x3006019,
2865  0x80000000,0x90000000,0x3006019,0x80000300,0x60198000,0x3,0x601980,0x0,0x3006,0x1980000,0x60000000,0x0,0x0,0xe0000000,0x0,
2866  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2867  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000000,
2868  0x0,0x0,0x0,0x0,0x0,0xc800019,0x80000000,0x198000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x1001,0x420000,0x0,0x0,0x90000000,
2869  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18000c06,0xc80001,0x10000000,0x18000c06,0x1800,0xc060000,0xc818,0xc0600,0xc8000000,
2870  0x18000,0xc0600000,0xc000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80660207,0x800f8060,0x300c004,0x0,0x6,
2871  0xe00703f,0x3f00383,0xf80f07fc,0x1f01f000,0x0,0xf8,0x607f,0x7c7e07,0xfe7fe0f8,0x6063fc1f,0x86066007,0xe7060f0,0x7f80f07f,
2872  0x81f8fff6,0x6606c03,0x70ee077f,0xe0786000,0xf0070000,0xc000060,0xc0,0x3e000,0x60006003,0x600fc00,0x0,0x0,0x0,0x0,0x0,0x3c0603,
2873  0xc0000000,0x7800000,0xf0000,0x0,0xf00001f,0x80001fe0,0x7fe000,0x0,0x0,0x0,0x168fe609,0x0,0x90e07,0x6000,0x3c000e,0x70000f8,
2874  0x1980001f,0x0,0x1f8,0xf00000f,0xf00180,0xfe000,0xe00e,0x1001,0x20060,0x6006006,0x600600,0x600fe07c,0x7fe7fe7f,0xe7fe3fc3,
2875  0xfc3fc3fc,0x7e07060f,0xf00f00,0xf00f0000,0xf360660,0x6606606e,0x76001e0,0xc00180f,0x1681981,0x10000000,0xc00180f,0x1980c00,
2876  0x180f0198,0x3801680c,0x180f01,0x68198000,0xc001,0x80f01980,0x18600198,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,
2877  0x8044020c,0xc01f8060,0x2004004,0x0,0xc,0x3f81f07f,0x87f80383,0xf81f87fc,0x3f83f800,0x0,0x1fc,0x780607f,0x81fe7f87,0xfe7fe1fc,
2878  0x6063fc1f,0x860c6007,0xe7061f8,0x7fc1f87f,0xc3fcfff6,0x6606c03,0x30c6067f,0xe0783000,0xf00d8000,0x6000060,0xc0,0x7e000,0x60006003,
2879  0x600fc00,0x0,0x0,0xc00,0x0,0x0,0x7c0603,0xe0000000,0xfc00000,0x1f0000,0x0,0x900003f,0xc0003fe0,0x7fe000,0x0,0x0,0x0,0x1302660f,
2880  0x0,0xf0606,0x6004,0x7e0006,0x60601f8,0x19800001,0x80000000,0x1f8,0x19800010,0x81080300,0x3f2000,0x2011,0x1001,0x1c0060,0x6006006,
2881  0x600600,0x601fe1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f87061f,0x81f81f81,0xf81f8000,0x3fa60660,0x66066066,0x66003f0,0x6003009,
2882  0x1301981,0x10000000,0x6003009,0x1980600,0x30090198,0x1f013006,0x300901,0x30198000,0x6003,0x901980,0x30600198,0x0,0x0,0x0,
2883  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc0f8c,0xc0180060,0x6006044,0x40000000,0xc,0x3181b041,0xc41c0783,0x388018,
2884  0x71c71800,0x0,0x106,0x18c0f061,0xc38261c6,0x600384,0x60606001,0x86186007,0xe78630c,0x60e30c60,0xe7040606,0x630cc03,0x39c30c00,
2885  0xc0603000,0x3018c000,0x3000060,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,0x60000000,0x18400000,0x180000,
2886  0x0,0x19800070,0x40003600,0xc000,0x0,0x0,0x0,0x25a06,0x0,0x6030c,0x4,0xe20007,0xe060180,0xf000,0x80000000,0xf0000,0x10800000,
2887  0x80080600,0x7f2000,0x2020,0x80001001,0x20000,0xf00f00f,0xf00f00,0x601b0382,0x60060060,0x6000600,0x60060060,0x61c78630,0xc30c30c3,
2888  0xc30c000,0x30e60660,0x66066063,0xc600738,0x3006019,0x80000000,0xe0000000,0x3006019,0x80000300,0x60198000,0x3e000003,0x601980,
2889  0x0,0x3006,0x1980000,0x60600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x80cc1fcc,0xc0180060,0x6006035,0x80000000,
2890  0x18,0x71c03000,0xc00c0583,0x300018,0x60c60c00,0x0,0x6,0x3060f060,0xc30060c6,0x600300,0x60606001,0x86306007,0x9e78670e,0x60670e60,
2891  0x66000606,0x630c606,0x19830c01,0xc0601800,0x30306000,0x60,0xc0,0x60000,0x60000000,0x6000c00,0x0,0x0,0xc00,0x0,0x0,0x600600,
2892  0x60000000,0x18000000,0x300000,0x0,0x78060,0x6600,0x1c000,0x300c,0x39819c0,0x0,0x25a00,0x0,0x30c,0x4,0xc00003,0xc060180,0x30c1f,
2893  0x80000000,0x30c000,0x10800001,0x80700000,0x7f2000,0x2020,0x80001001,0x20060,0xf00f00f,0xf00f00,0xf01b0300,0x60060060,0x6000600,
2894  0x60060060,0x60c78670,0xe70e70e7,0xe70e000,0x70c60660,0x66066063,0xc7f8618,0x0,0x0,0x0,0x0,0x0,0x0,0x7000000,0x0,0x0,0x0,
2895  0x0,0x600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6019,0x87ff3a4c,0xc0180060,0x400600e,0x600000,0x18,0x60c03000,
2896  0xc00c0d83,0x700018,0x60c60c00,0x20,0x400006,0x3060f060,0xc6006066,0x600600,0x60606001,0x86606006,0x966c6606,0x60660660,0x66000606,
2897  0x630c666,0xf019801,0x80601800,0x30603000,0x1f06f,0xf01ec0,0xf03fe1ec,0x6703e01f,0x61c0c06,0xdc6701f0,0x6f01ec0c,0xe1f87fc6,
2898  0xc60cc03,0x71c60c7f,0xc0600600,0x60000000,0x30000000,0x300000,0x40040,0x88060,0x6600,0x18000,0x300c,0x1981980,0x0,0x2421f,
2899  0x80003ce0,0x7fc198,0x601f,0xc02021,0x980600c0,0x40230,0x80000000,0x402000,0x19806003,0x80006,0xc7f2000,0x2020,0x80001001,
2900  0x420060,0xf00f00f,0xf00f00,0xf01b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x6606208,0x60e60660,0x66066061,
2901  0x987fc670,0x1f01f01f,0x1f01f01,0xf039c0f0,0xf00f00f,0xf03e03,0xe03e03e0,0x1f06701f,0x1f01f01,0xf01f0060,0x1e660c60,0xc60c60c6,
2902  0xc6f060c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x7ff3207,0x8c0c0000,0xc00300e,0x600000,0x30,0x60c03000,
2903  0xc01c0983,0xf0600030,0x31860c06,0x6001e0,0x78000e,0x23e1f861,0xc6006066,0x600600,0x60606001,0x86c06006,0x966c6606,0x60660660,
2904  0xe7000606,0x630c666,0xf01f803,0x600c00,0x30000000,0x3f87f,0x83f83fc3,0xf83fe3fc,0x7f83e01f,0x6380c07,0xfe7f83f8,0x7f83fc0d,
2905  0xf3fc7fc6,0xc71cc03,0x3183187f,0xc0600600,0x60000000,0xff806000,0x300000,0x40040,0x88070,0x6600,0x60030060,0x6001818,0x1883180,
2906  0x0,0x2423f,0xc0007ff0,0x607fc1f8,0x603f,0x80c01fc1,0xf80601e0,0x5f220,0x80420000,0x5f2000,0xf006006,0x80006,0xc7f2000,0x2020,
2907  0x82107c07,0xc03c0060,0x1f81f81f,0x81f81f80,0xf03b0600,0x60060060,0x6000600,0x60060060,0x6066c660,0x66066066,0x660671c,0x61660660,
2908  0x66066061,0xf860e6c0,0x3f83f83f,0x83f83f83,0xf87fe3f8,0x3f83f83f,0x83f83e03,0xe03e03e0,0x3f87f83f,0x83f83f83,0xf83f8060,
2909  0x3fc60c60,0xc60c60c3,0x187f8318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x883200,0x300c0000,0xc003035,0x80600000,
2910  0x30,0x66c03001,0xc0f81983,0xf86f0030,0x1f071c06,0x600787,0xfe1e001c,0x6261987f,0x86006067,0xfe7fc600,0x7fe06001,0x87c06006,
2911  0xf6646606,0x60e6067f,0xc3e00606,0x61986f6,0x600f007,0x600c00,0x30000000,0x21c71,0x830831c3,0x1c06031c,0x71c06003,0x6700c06,
2912  0x6671c318,0x71831c0f,0x16040c06,0xc318606,0x1b031803,0x80600600,0x60000000,0x30009000,0x300000,0x40040,0x7003e,0x67e0,0x90070090,
2913  0x9001818,0x8c3100,0x0,0x60,0x4000e730,0x900380f0,0x6034,0x80c018c7,0xfe060338,0xb0121,0x80c60000,0x909000,0x6008,0x1080006,
2914  0xc3f2000,0x2011,0x3180060,0x60060e0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0x60664660,0x66066066,
2915  0x66063b8,0x62660660,0x66066060,0xf06066c0,0x21c21c21,0xc21c21c2,0x1c466308,0x31c31c31,0xc31c0600,0x60060060,0x31871c31,0x83183183,
2916  0x18318000,0x71860c60,0xc60c60c3,0x18718318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1981a00,0xe03e0000,0xc003044,
2917  0x40600000,0x60,0x66c03001,0x80f03182,0x1c7f8030,0x3f83fc06,0x601e07,0xfe078038,0x6661987f,0x86006067,0xfe7fc61e,0x7fe06001,
2918  0x87e06006,0x66666606,0x7fc6067f,0x81f80606,0x61986f6,0x6006006,0x600600,0x30000000,0xc60,0xc60060c6,0xc06060c,0x60c06003,
2919  0x6e00c06,0x6660c60c,0x60c60c0e,0x6000c06,0xc318666,0x1f031803,0x600600,0x603c2000,0x30016800,0x1fe0000,0x1f81f8,0x1c1f,0x804067e1,
2920  0x68060168,0x16800810,0xc42300,0x0,0x60,0x20c331,0x68030060,0x6064,0x3fc1040,0xf006031c,0xa011e,0x818c7fe0,0x909000,0x7fe1f,
2921  0x80f00006,0xc0f2060,0xf80e,0x18c0780,0x780781c0,0x19819819,0x81981981,0x9833c600,0x7fe7fe7f,0xe7fe0600,0x60060060,0xfc666660,
2922  0x66066066,0x66061f0,0x66660660,0x66066060,0x606066e0,0xc00c00,0xc00c00c0,0xc066600,0x60c60c60,0xc60c0600,0x60060060,0x60c60c60,
2923  0xc60c60c6,0xc60c000,0x61c60c60,0xc60c60c3,0x1860c318,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x1980f81,0x80373000,
2924  0xc003004,0x7fe0001,0xf0000060,0x60c03003,0x183180,0xc71c060,0x3181ec00,0x7000,0xe070,0x66619860,0xc6006066,0x60061e,0x60606001,
2925  0x87606006,0x66626606,0x7f860661,0xc01c0606,0x6198696,0xf00600e,0x600600,0x30000000,0x1fc60,0xc60060c7,0xfc06060c,0x60c06003,
2926  0x7c00c06,0x6660c60c,0x60c60c0c,0x7f00c06,0xc3b8666,0xe01b007,0x3c00600,0x3c7fe000,0xff03ec00,0x1fe0000,0x40040,0xe001,0xc0806603,
2927  0xec0e03ec,0x3ec00010,0x0,0x60000000,0x7f,0x10c3f3,0xec070060,0x6064,0x3fc1040,0x6000030c,0xa0100,0x3187fe1,0xf09f1000,0x7fe00,
2928  0x6,0xc012060,0x0,0xc63c03,0xc03c0380,0x19819819,0x81981981,0x98330600,0x60060060,0x6000600,0x60060060,0xfc662660,0x66066066,
2929  0x66060e0,0x6c660660,0x66066060,0x6060e630,0x1fc1fc1f,0xc1fc1fc1,0xfc3fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6,
2930  0xc60c7fe,0x62c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe02c6,0x3c633000,0xc003004,
2931  0x7fe0001,0xf00000c0,0x60c03006,0xc6180,0xc60c060,0x60c00c00,0x7000,0xe060,0x66639c60,0x66006066,0x600606,0x60606001,0x86306006,
2932  0x66636606,0x60060660,0xc0060606,0x61f8696,0xf00600c,0x600300,0x30000000,0x3fc60,0xc60060c7,0xfc06060c,0x60c06003,0x7c00c06,
2933  0x6660c60c,0x60c60c0c,0x1f80c06,0xc1b0666,0xe01b00e,0x3c00600,0x3c43c000,0x3007de00,0x600000,0x40040,0x30000,0x61006607,0xde0c07de,
2934  0x7de00000,0x0,0xf07fefff,0x1f,0x8008c3f7,0xde0e0060,0x6064,0xc01047,0xfe00018c,0xb013f,0x86300061,0xf0911000,0x6000,0x6,
2935  0xc012060,0x3f,0x8063c0cc,0x3cc0c700,0x39c39c39,0xc39c39c1,0x98630600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,
2936  0x66061f0,0x78660660,0x66066060,0x607fc618,0x3fc3fc3f,0xc3fc3fc3,0xfc7fe600,0x7fc7fc7f,0xc7fc0600,0x60060060,0x60c60c60,0xc60c60c6,
2937  0xc60c7fe,0x64c60c60,0xc60c60c1,0xb060c1b0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0xffe0260,0x6661b000,0xc003000,
2938  0x600000,0xc0,0x60c0300c,0xc7fe0,0xc60c060,0x60c01c00,0x1e07,0xfe078060,0x6663fc60,0x66006066,0x600606,0x60606001,0x86386006,
2939  0x6636606,0x60060660,0xe0060606,0x60f039c,0x1b806018,0x600300,0x30000000,0x70c60,0xc60060c6,0x6060c,0x60c06003,0x7600c06,
2940  0x6660c60c,0x60c60c0c,0x1c0c06,0xc1b03fc,0xe01f01c,0xe00600,0x70000000,0x3007fc00,0x600000,0x40040,0x0,0x62006607,0xfc1807fc,
2941  0x7fc00000,0x0,0xf0000000,0x1,0xc004c307,0xfc1c0060,0x6064,0xc018c0,0x600000d8,0x5f200,0x3180060,0x50a000,0x6000,0x6,0xc012000,
2942  0x0,0xc601c0,0x4201c600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0600,0x60060060,0x6000600,0x60060060,0x60663660,0x66066066,0x66063b8,
2943  0x70660660,0x66066060,0x607f860c,0x70c70c70,0xc70c70c7,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,
2944  0x68c60c60,0xc60c60c1,0xf060c1f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3300260,0x6661e000,0xc003000,0x600000,
2945  0x180,0x71c03018,0xc7fe0,0xc60c0c0,0x60c01800,0x787,0xfe1e0060,0x6663fc60,0x630060c6,0x600306,0x60606001,0x86186006,0x661e70e,
2946  0x60070c60,0x60060606,0x60f039c,0x19806038,0x600180,0x30000000,0x60c60,0xc60060c6,0x6060c,0x60c06003,0x6700c06,0x6660c60c,
2947  0x60c60c0c,0xc0c06,0xc1b039c,0x1f00e018,0x600600,0x60000000,0x1803f800,0x600000,0x40040,0x39e00,0x63006603,0xf83803f8,0x3f800000,
2948  0x0,0x60000000,0x0,0xc00cc303,0xf8180060,0x6064,0xc01fc0,0x60060070,0x40200,0x18c0060,0x402000,0x6000,0x6,0xc012000,0x0,0x18c0140,
2949  0x2014600,0x3fc3fc3f,0xc3fc3fc3,0xfc7f0300,0x60060060,0x6000600,0x60060060,0x60c61e70,0xe70e70e7,0xe70e71c,0x60e60660,0x66066060,
2950  0x6060060c,0x60c60c60,0xc60c60c6,0xcc60600,0x60060060,0x6000600,0x60060060,0x60c60c60,0xc60c60c6,0xc60c000,0x70c60c60,0xc60c60c0,
2951  0xe060c0e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x33022e0,0x6670c000,0xc003000,0x600600,0x60180,0x31803030,
2952  0x41c0184,0x1831c0c0,0x71c23806,0x6001e0,0x780000,0x62630c60,0xe38261c6,0x600386,0x60606043,0x860c6006,0x661e30c,0x60030c60,
2953  0x740e0607,0xe0f039c,0x31c06030,0x600180,0x30000000,0x61c71,0x830831c3,0x406031c,0x60c06003,0x6300c06,0x6660c318,0x71831c0c,
2954  0x41c0c07,0x1c0e039c,0x1b00e030,0x600600,0x60000000,0x1c41b00e,0x601cc0,0x401f8,0x45240,0xe1803601,0xb03001b0,0x1b000000,
2955  0x0,0x0,0x41,0xc008e711,0xb0300060,0x6034,0x80c02020,0x60060030,0x30c00,0xc60000,0x30c000,0x0,0x7,0x1c012000,0x0,0x3180240,
2956  0x6024608,0x30c30c30,0xc30c30c3,0xc630382,0x60060060,0x6000600,0x60060060,0x61c61e30,0xc30c30c3,0xc30c208,0x70c70e70,0xe70e70e0,
2957  0x6060068c,0x61c61c61,0xc61c61c6,0x1cc62308,0x30430430,0x43040600,0x60060060,0x31860c31,0x83183183,0x18318060,0x31c71c71,
2958  0xc71c71c0,0xe07180e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2203fc0,0x663f6000,0x6006000,0x600600,0x60300,
2959  0x3f81fe7f,0xc7f80187,0xf83f80c0,0x3f83f006,0x600020,0x400060,0x33e6067f,0xc1fe7f87,0xfe6001fe,0x6063fc7f,0x60e7fe6,0x660e3f8,
2960  0x6001f860,0x37fc0603,0xfc06030c,0x30c0607f,0xe06000c0,0x30000000,0x7fc7f,0x83f83fc3,0xfc0603fc,0x60c7fe03,0x61807c6,0x6660c3f8,
2961  0x7f83fc0c,0x7f80fc3,0xfc0e039c,0x3180607f,0xc0600600,0x60000000,0xfc0e00c,0x601986,0x66040040,0x4527f,0xc0803fe0,0xe07fe0e0,
2962  0xe000000,0x0,0x0,0x7f,0x80107ff0,0xe07fc060,0x603f,0x83fe0000,0x60060018,0xf000,0x420000,0xf0000,0x7fe00,0x7,0xfe012000,
2963  0x0,0x2100640,0xc0643f8,0x60660660,0x66066067,0xec3e1fe,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7f860e3f,0x83f83f83,0xf83f8000,
2964  0x5fc3fc3f,0xc3fc3fc0,0x606006fc,0x7fc7fc7f,0xc7fc7fc7,0xfcffe3f8,0x3fc3fc3f,0xc3fc7fe7,0xfe7fe7fe,0x3f860c3f,0x83f83f83,
2965  0xf83f8060,0x7f83fc3f,0xc3fc3fc0,0x607f8060,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x6000,0x2201f80,0x3c1e7000,0x6006000,
2966  0x600,0x60300,0xe01fe7f,0xc3f00183,0xe01f0180,0x1f01e006,0x600000,0x60,0x3006067f,0x807c7e07,0xfe6000f8,0x6063fc3e,0x6067fe6,
2967  0x660e0f0,0x6000f060,0x3bf80601,0xf806030c,0x60e0607f,0xe06000c0,0x30000000,0x1ec6f,0xf01ec0,0xf80601ec,0x60c7fe03,0x61c03c6,
2968  0x6660c1f0,0x6f01ec0c,0x3f007c1,0xcc0e030c,0x71c0c07f,0xc0600600,0x60000000,0x7804018,0xe01186,0x66040040,0x39e3f,0x80401fe0,
2969  0x407fe040,0x4000000,0x0,0x0,0x3f,0x203ce0,0x407fc060,0x601f,0x3fe0000,0x60060018,0x0,0x0,0x0,0x7fe00,0x6,0xe6012000,0x0,
2970  0x7e0,0x1807e1f0,0x60660660,0x66066066,0x6c3e07c,0x7fe7fe7f,0xe7fe3fc3,0xfc3fc3fc,0x7e060e0f,0xf00f00,0xf00f0000,0x8f01f81f,
2971  0x81f81f80,0x60600670,0x1ec1ec1e,0xc1ec1ec1,0xec79c0f0,0xf80f80f,0x80f87fe7,0xfe7fe7fe,0x1f060c1f,0x1f01f01,0xf01f0000,0x4f01cc1c,
2972  0xc1cc1cc0,0xc06f00c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x6006000,0x600,0x600,0x0,0x0,0x0,0x0,
2973  0x600000,0x0,0x18000000,0x0,0x0,0x0,0x0,0x0,0x1800,0x0,0x0,0x0,0x600060,0x30000000,0x0,0x0,0xc,0x3,0x0,0x0,0x60000c00,0x0,
2974  0x0,0xc000,0x600600,0x60000000,0x18,0xc03100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f8,0x0,0x0,0x0,0x0,0x6,
2975  0x12000,0x2000000,0x40,0x20004000,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2976  0x0,0xc06000c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x2004000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,
2977  0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0xc00,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x21c,0x3,0x0,0x0,0x60000c00,0x0,0x0,0xc000,
2978  0x7c0603,0xe0000000,0x10,0xc02300,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x601f0,0x0,0x0,0x0,0x0,0x6,0x12000,0x1000000,
2979  0x40,0x7e004000,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc06000c0,0x0,
2980  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x300c000,0xc00,0x0,0x0,0x0,0x0,0x0,0xc00000,0x0,0x7800000,0x0,
2981  0x0,0x0,0x0,0x0,0x800,0x0,0x0,0x0,0x780000,0xf0000000,0x0,0x0,0x3f8,0x3e,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x3c0603,0xc0000000,
2982  0x10,0xfc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x60000,0x0,0x0,0x0,0x0,0x6,0x0,0x1000000,0x0,0x0,0x0,0x0,
2983  0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2984  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,
2985  0x0,0x1f0,0x3c,0x0,0x0,0x60000c00,0x0,0x0,0x38000,0x600,0x0,0x0,0xf000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2986  0x0,0x0,0x0,0x0,0x0,0x6,0x0,0xe000000,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x0,0x0,0x0,0x0,
2987  0x0,0x0,0x0,0x3,0x80600380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2988  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2989  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2990  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
2991 
2992  // Define a 16x32 font (large size).
2993  const unsigned int font16x32[16*32*256/32] = {
2994  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2995  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2996  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2997  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x70000e0,0x3c00730,0xe7001c0,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0x730,0x70000e0,0x3c00730,
2998  0xe700000,0x700,0xe003c0,0xe7000e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
2999  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3000  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3001  0x0,0x0,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3002  0x0,0x0,0x18001c0,0x6600ff0,0xe7003e0,0x0,0x18001c0,0x6600e70,0x18001c0,0x6600e70,0xff0,0x18001c0,0x6600ff0,0xe700000,0x180,
3003  0x1c00660,0xe7001c0,0x0,0x0,0x0,0x380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3004  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3005  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,
3006  0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c00380,
3007  0xc300ce0,0xe700630,0x0,0x1c00380,0xc300e70,0x1c00380,0xc300e70,0xce0,0x1c00380,0xc300ce0,0xe700000,0x1c0,0x3800c30,0xe700380,
3008  0x0,0x0,0x0,0x7c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3009  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3010  0x0,0x0,0x0,0x0,0xe000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,
3011  0x0,0x0,0x0,0x0,0x0,0xc300000,0x0,0xc300000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x700000,0x0,0x0,0x0,0x7c007c00,0x3e000000,
3012  0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe000070,0x1800000,0xc60,0x0,0xe000070,0x1800000,0xe000070,
3013  0x1800000,0x0,0xe000070,0x1800000,0x0,0xe00,0x700180,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3014  0x0,0x0,0x0,0x800000,0x0,0x600600,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3015  0x0,0x0,0x3f0,0xfc0,0x0,0x7000000,0x38000000,0x1c0000,0xfc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,
3016  0x1801f00,0x0,0x0,0x1c,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7300000,0x6600000,0x0,0x6600000,0x0,0x0,0x0,0x0,0xe700000,
3017  0x0,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0x0,0xc000c00,0x43800000,0x0,0x0,0x630,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3018  0xf80,0x70000e0,0x3c00730,0xe700c60,0x0,0x70000e0,0x3c00e70,0x70000e0,0x3c00e70,0xe000730,0x70000e0,0x3c00730,0xe700000,0x700,
3019  0xe003c0,0xe7000e0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300000,0x803c00,0x7c00180,
3020  0xc00300,0x1000000,0x0,0x1c,0x3c007c0,0xfc007e0,0xe01ff8,0x3f03ffc,0x7e007c0,0x0,0x0,0x7c0,0x1c0,0x7f8003f0,0x7f007ff8,0x7ff803f0,
3021  0x70381ffc,0xff0700e,0x7000783c,0x783807c0,0x7fc007c0,0x7fc00fc0,0x7fff7038,0x700ee007,0x780f780f,0x7ffc03f0,0x70000fc0,0x3c00000,
3022  0x3000000,0x38000000,0x1c0000,0x1fc0000,0x380001c0,0xe01c00,0x7f800000,0x0,0x0,0x0,0x0,0x0,0x0,0xfc,0x1801f80,0x0,0x1f80000,
3023  0x7e,0x0,0x0,0x2400000,0xfc00000,0x7ff0000,0x7ffc0000,0x0,0x0,0x0,0x0,0xf30fb0c,0x2400000,0x0,0x240780f,0x1c0,0xfc,0x780f,
3024  0x18003f0,0xe700000,0x7c00000,0x0,0xff0,0x3c00000,0x78007c0,0xc00000,0xff80000,0xf80,0x7c00000,0xc000c00,0x18001c0,0x1c001c0,
3025  0x1c001c0,0x1c003e0,0x7fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007838,0x7c007c0,0x7c007c0,0x7c00000,0x7c67038,
3026  0x70387038,0x7038780f,0x70001fe0,0x30000c0,0x2400f30,0xe700c60,0x0,0x30000c0,0x2400e70,0x30000c0,0x2400e70,0xf700f30,0x30000c0,
3027  0x2400f30,0xe700000,0x300,0xc00240,0xe7000c0,0x38000e70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,
3028  0x630018c,0x807e00,0xfe00180,0xc00300,0x1000000,0x0,0x38,0xff01fc0,0x3ff01ff0,0x1e01ff8,0x7f83ffc,0x1ff80ff0,0x0,0x0,0xff0,
3029  0x1f003e0,0x7fe00ff8,0x7fc07ff8,0x7ff80ff8,0x70381ffc,0xff0701c,0x7000783c,0x78381ff0,0x7fe01ff0,0x7fe01ff0,0x7fff7038,0x781ee007,
3030  0x3c1e380e,0x7ffc0380,0x380001c0,0x3c00000,0x1800000,0x38000000,0x1c0000,0x3c00000,0x380001c0,0xe01c00,0x3800000,0x0,0x0,
3031  0x0,0x7000000,0x0,0x0,0x1e0,0x18003c0,0x0,0x3fc0000,0x70,0x0,0x0,0x6600000,0x1ff00000,0x1fff0000,0x7ffc0000,0x0,0x0,0x0,0x0,
3032  0xcf0239c,0x3c00000,0x0,0x3c0380e,0x1c0,0x2001fe,0x380e,0x18007f8,0xe700000,0x8600000,0x0,0xff0,0x7e00000,0x8c00870,0x1800000,
3033  0x1ff80000,0x180,0xc600000,0xc000c00,0x38001c0,0x3e003e0,0x3e003e0,0x3e001c0,0x7fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,
3034  0x7fc07838,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x1fec7038,0x70387038,0x7038380e,0x70003ce0,0x1800180,0x6600cf0,0xe7007c0,0x0,
3035  0x1800180,0x6600e70,0x1800180,0x6600e70,0x7c00cf0,0x1800180,0x6600cf0,0xe700000,0x180,0x1800660,0xe700180,0x38000e70,0x0,
3036  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630030c,0x3f0e700,0x1e200180,0x1800180,0x21100000,0x0,
3037  0x38,0x1e7819c0,0x38781038,0x1e01c00,0xf080038,0x1c381c38,0x0,0x0,0x1878,0x7fc03e0,0x70e01e18,0x70e07000,0x70001e18,0x703801c0,
3038  0x707038,0x70007c7c,0x7c381c70,0x70701c70,0x70703830,0x1c07038,0x381ce007,0x1c1c3c1e,0x3c0380,0x380001c0,0x7e00000,0xc00000,
3039  0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,0x70c0000,0xe0,
3040  0x0,0x0,0xc300000,0x38300000,0x3c700000,0x3c0000,0x0,0x0,0x0,0x0,0xce022f4,0x1800000,0x0,0x1803c1e,0x1c0,0x2003c2,0x3c1e,
3041  0x1800e08,0x7e0,0x300000,0x0,0x7e00000,0xe700000,0x600030,0x3000000,0x3f980000,0x180,0x18200000,0xc000c00,0x1e0001c0,0x3e003e0,
3042  0x3e003e0,0x3e003e0,0xfe01e18,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70e07c38,0x1c701c70,0x1c701c70,0x1c700000,0x3c787038,
3043  0x70387038,0x70383c1e,0x70003870,0xc00300,0xc300ce0,0x380,0x0,0xc00300,0xc300000,0xc00300,0xc300000,0xfc00ce0,0xc00300,0xc300ce0,
3044  0x0,0xc0,0x3000c30,0x300,0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630031c,0xff8c300,
3045  0x1c000180,0x1800180,0x39380000,0x0,0x70,0x1c3801c0,0x203c001c,0x3e01c00,0x1c000038,0x381c3838,0x0,0x0,0x1038,0xe0e03e0,0x70703c08,
3046  0x70707000,0x70003808,0x703801c0,0x707070,0x70007c7c,0x7c383838,0x70383838,0x70387010,0x1c07038,0x381c700e,0x1e3c1c1c,0x780380,
3047  0x1c0001c0,0xe700000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,
3048  0x0,0xe000000,0xe0,0x0,0x1000100,0x3800,0x70100000,0x38700000,0x780000,0x1c0,0x7801ce0,0xe380000,0x0,0x2264,0x0,0x0,0x1c1c,
3049  0x0,0x200780,0x1c1c,0x1800c00,0x1818,0x7f00000,0x0,0x18180000,0xc300000,0x600070,0x0,0x7f980000,0x180,0x18300000,0xc000c00,
3050  0x3000000,0x3e003e0,0x3e003e0,0x3e003e0,0xee03c08,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,
3051  0x38380000,0x38387038,0x70387038,0x70381c1c,0x7fc03870,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xbc00000,0x0,0x0,0x0,0x0,0x0,0x0,
3052  0x38000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0xe88c300,0x1c000180,0x38001c0,
3053  0xfe00180,0x0,0x70,0x1c3801c0,0x1c001c,0x6e01c00,0x1c000078,0x381c3818,0x0,0x40000,0x40000038,0x1c0607e0,0x70703800,0x70707000,
3054  0x70003800,0x703801c0,0x7070e0,0x70007c7c,0x7c383838,0x70383838,0x70387000,0x1c07038,0x381c700e,0xf780e38,0x700380,0x1c0001c0,
3055  0x1c380000,0x0,0x38000000,0x1c0000,0x3800000,0x38000000,0x1c00,0x3800000,0x0,0x0,0x0,0x7000000,0x0,0x0,0x1c0,0x18001c0,0x0,
3056  0xe000000,0xe0,0x0,0x1000100,0x4400,0x70000000,0x38700000,0x700000,0xe0,0x7001c70,0xe380000,0x0,0x2264,0x0,0x0,0xe38,0x0,
3057  0x200700,0xe38,0x1800c00,0x300c,0xc300000,0x0,0x300c0000,0xc300180,0x6003c0,0x0,0x7f980000,0x180,0x18300000,0xc000c00,0x1800000,
3058  0x7e007e0,0x7e007e0,0x7e003e0,0xee03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70707c38,0x38383838,0x38383838,0x38380000,
3059  0x38387038,0x70387038,0x70380e38,0x7ff039f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x40000,0x0,0x0,0x38000000,
3060  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6300318,0x1c80e700,0x1c000180,0x38001c0,0x3800180,
3061  0x0,0xe0,0x381c01c0,0x1c001c,0x6e01c00,0x38000070,0x381c381c,0x0,0x3c0000,0x78000078,0x38030770,0x70707800,0x70387000,0x70007000,
3062  0x703801c0,0x7071c0,0x7000745c,0x7638701c,0x7038701c,0x70387000,0x1c07038,0x1c38718e,0x7700f78,0xf00380,0xe0001c0,0x381c0000,
3063  0x7e0,0x39e003e0,0x79c03f0,0x3ffc079c,0x39e01fc0,0xfe01c1e,0x3807778,0x39e007e0,0x39e0079c,0x73c07e0,0x7ff83838,0x701ce007,
3064  0x783c701c,0x1ffc01c0,0x18001c0,0x0,0x1c000100,0xe0,0x0,0x1000100,0x4200,0x70000000,0x70700100,0xf00100,0x10000e0,0x7000c70,
3065  0xc700000,0x0,0x2204,0x7e00000,0x1e380100,0x1ffc0f78,0x0,0xf80700,0xf78,0x1800e00,0x63e6,0x18300000,0x0,0x6fe60000,0xe700180,
3066  0xc00060,0x3838,0x7f980000,0x180,0x18300000,0xc000c00,0x18001c0,0x7700770,0x7700770,0x77007f0,0xee07800,0x70007000,0x70007000,
3067  0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1008,0x707c7038,0x70387038,0x70380f78,0x707039c0,0x7e007e0,0x7e007e0,
3068  0x7e007e0,0x1f3c03e0,0x3f003f0,0x3f003f0,0x1fc01fc0,0x1fc01fc0,0x7f039e0,0x7e007e0,0x7e007e0,0x7e00380,0x7ce3838,0x38383838,
3069  0x3838701c,0x39e0701c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x6307fff,0x1c807e0c,0xe000180,
3070  0x30000c0,0x3800180,0x0,0xe0,0x381c01c0,0x1c001c,0xce01fe0,0x38000070,0x381c381c,0x3800380,0xfc0000,0x7e0000f0,0x30030770,
3071  0x70707000,0x70387000,0x70007000,0x703801c0,0x707380,0x700076dc,0x7638701c,0x7038701c,0x70387800,0x1c07038,0x1c3873ce,0x7f00770,
3072  0xe00380,0xe0001c0,0x700e0000,0x1ff8,0x3ff00ff0,0xffc0ff8,0x3ffc0ffc,0x3bf01fc0,0xfe01c3c,0x3807f78,0x3bf00ff0,0x3ff00ffc,
3073  0x77e0ff0,0x7ff83838,0x3838e007,0x3c783838,0x1ffc01c0,0x18001c0,0x0,0x7ff00380,0x1e0,0x0,0x1000100,0x4200,0x78000000,0x70700380,
3074  0xe00380,0x3800060,0xe000e30,0x1c600000,0x0,0x2204,0xff00000,0x7f7c0380,0x1ffc0770,0x1c0,0x3fc0700,0x18040770,0x1800780,0x4e12,
3075  0x18300104,0x0,0x4c320000,0x7e00180,0x1c00030,0x3838,0x7f980000,0x180,0x18302080,0xc000c00,0x18001c0,0x7700770,0x7700770,
3076  0x7700770,0x1ee07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c381c,0x705c7038,0x70387038,
3077  0x70380770,0x70383b80,0x1ff81ff8,0x1ff81ff8,0x1ff81ff8,0x3fbe0ff0,0xff80ff8,0xff80ff8,0x1fc01fc0,0x1fc01fc0,0xff83bf0,0xff00ff0,
3078  0xff00ff0,0xff00380,0xffc3838,0x38383838,0x38383838,0x3ff03838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3079  0x0,0x1c0,0x7fff,0x1c803c38,0xf000000,0x70000e0,0xfe00180,0x0,0x1c0,0x381c01c0,0x3c0078,0xce01ff0,0x39e000f0,0x1c38381c,0x3800380,
3080  0x3e07ffc,0xf8001f0,0x307b0770,0x70e07000,0x70387000,0x70007000,0x703801c0,0x707700,0x700076dc,0x7638701c,0x7038701c,0x70387e00,
3081  0x1c07038,0x1c3873ce,0x3e007f0,0x1e00380,0x70001c0,0x0,0x1038,0x3c381e18,0x1c7c1e3c,0x3801e3c,0x3c7801c0,0xe01c78,0x380739c,
3082  0x3c781c38,0x3c381c3c,0x7c21e10,0x7003838,0x3838700e,0x1ef03838,0x3c01c0,0x18001c0,0x0,0x7fe007c0,0x1c0,0x0,0x1000100,0x6400,
3083  0x7e000000,0x707007c0,0x1e007c0,0x7c00070,0xe000638,0x18600000,0x0,0x0,0x1e100000,0x73ce07c0,0x3c07f0,0x1c0,0x7240700,0x1ddc3ffe,
3084  0x1800de0,0x8c01,0x1870030c,0x0,0x8c310000,0x3c00180,0x3800030,0x3838,0x7f980000,0x180,0x183030c0,0xc000c00,0x430001c0,0x7700770,
3085  0x7700770,0x7700770,0x1ce07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x70387638,0x701c701c,0x701c701c,0x701c1c38,0x70dc7038,
3086  0x70387038,0x703807f0,0x70383b80,0x10381038,0x10381038,0x10381038,0x21e71e18,0x1e3c1e3c,0x1e3c1e3c,0x1c001c0,0x1c001c0,0x1e383c78,
3087  0x1c381c38,0x1c381c38,0x1c380380,0x1c383838,0x38383838,0x38383838,0x3c383838,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3088  0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0x1e8000e0,0x1f000000,0x70000e0,0x39380180,0x0,0x1c0,0x3b9c01c0,0x3c07f0,0x18e01078,0x3bf800e0,
3089  0x7e0383c,0x3800380,0x1f807ffc,0x3f001c0,0x61ff0e38,0x7fc07000,0x70387ff0,0x7ff07000,0x7ff801c0,0x707f00,0x7000729c,0x7338701c,
3090  0x7070701c,0x70703fc0,0x1c07038,0x1e7873ce,0x1c003e0,0x3c00380,0x70001c0,0x0,0x1c,0x3c381c00,0x1c3c1c1c,0x3801c3c,0x383801c0,
3091  0xe01cf0,0x380739c,0x38381c38,0x3c381c3c,0x7801c00,0x7003838,0x3838700e,0xfe03c78,0x7801c0,0x18001c0,0x0,0x1c000c20,0xff8,
3092  0x0,0x1ff01ff0,0x3818,0x3fc00100,0x707e0c20,0x3c00c20,0xc200030,0xc000618,0x18c00000,0x0,0x0,0x1c000080,0xe1ce0c20,0x7803e0,
3093  0x1c0,0xe200700,0xff83ffe,0x1801878,0x9801,0x1cf0071c,0x7ffc0000,0x8c310000,0x7ffe,0x7000030,0x3838,0x3f980380,0x180,0xc6038e0,
3094  0x7f9c7f9c,0x3e1c01c0,0xe380e38,0xe380e38,0xe380f78,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,0x1c001c0,0xfe387338,0x701c701c,
3095  0x701c701c,0x701c0e70,0x719c7038,0x70387038,0x703803e0,0x70383b80,0x1c001c,0x1c001c,0x1c001c,0xe71c00,0x1c1c1c1c,0x1c1c1c1c,
3096  0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380000,0x3c383838,0x38383838,0x38383c78,0x3c383c78,0x0,0x0,0x0,0x0,
3097  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x630,0xf800380,0x3f830000,0x70000e0,0x31080180,0x0,0x380,0x3b9c01c0,
3098  0x7807e0,0x38e00038,0x3c3800e0,0xff01c3c,0x3800380,0x7c000000,0x7c03c0,0x61870e38,0x7fc07000,0x70387ff0,0x7ff070fc,0x7ff801c0,
3099  0x707f80,0x7000739c,0x7338701c,0x7ff0701c,0x7fe00ff0,0x1c07038,0xe7073ce,0x1c003e0,0x3800380,0x38001c0,0x0,0x1c,0x381c3800,
3100  0x381c380e,0x380381c,0x383801c0,0xe01de0,0x380739c,0x3838381c,0x381c381c,0x7001e00,0x7003838,0x1c70718e,0x7e01c70,0xf00380,
3101  0x18001e0,0x1e000000,0x1c001bb0,0xff8,0x0,0x1000100,0xe0,0xff00300,0x707e1bb0,0x3801bb0,0x1bb00010,0x8000308,0x30c00000,0x0,
3102  0x0,0x1e0000c0,0xe1ce1bb0,0xf003e0,0x1c0,0x1c203ff8,0x63003e0,0x180181c,0x9801,0xfb00e38,0x7ffc0000,0x8fc10000,0x7ffe,0xe000860,
3103  0x3838,0x1f980380,0x180,0x7c01c70,0x1f001f0,0x1f003c0,0xe380e38,0xe380e38,0xe380e38,0x1cfc7000,0x7ff07ff0,0x7ff07ff0,0x1c001c0,
3104  0x1c001c0,0xfe387338,0x701c701c,0x701c701c,0x701c07e0,0x731c7038,0x70387038,0x703803e0,0x70383980,0x1c001c,0x1c001c,0x1c001c,
3105  0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x387c3838,0x38383838,0x38381c70,
3106  0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc30,0x7f00e00,0x33c30000,0x70000e0,0x1007ffe,
3107  0x0,0x380,0x3b9c01c0,0xf00078,0x30e0001c,0x3c1c01c0,0x1c381fdc,0x0,0x70000000,0x1c0380,0x63030e38,0x70707000,0x70387000,0x700070fc,
3108  0x703801c0,0x707b80,0x7000739c,0x7338701c,0x7fc0701c,0x7fc001f0,0x1c07038,0xe703e5c,0x3e001c0,0x7800380,0x38001c0,0x0,0x7fc,
3109  0x381c3800,0x381c380e,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7001fc0,0x7003838,0x1c70718e,0x7c01c70,
3110  0xe01f00,0x180007c,0x7f8c0000,0x7fc03fb8,0x1c0,0x0,0x1000100,0x700,0x1f00600,0x70703fb8,0x7803fb8,0x3fb80000,0x8000000,0x180,
3111  0x0,0x0,0x1fc00060,0xe1ce3fb8,0xe001c0,0x1c0,0x1c203ff8,0xc1801c0,0x180c,0x9801,0x1c70,0xc0000,0x8cc10000,0x180,0xfe007c0,
3112  0x3838,0x7980380,0xff0,0xe38,0x3e003e00,0x3e000380,0xe380e38,0xe380e38,0xe380e38,0x38e07000,0x70007000,0x70007000,0x1c001c0,
3113  0x1c001c0,0x70387338,0x701c701c,0x701c701c,0x701c03c0,0x731c7038,0x70387038,0x703801c0,0x703838e0,0x7fc07fc,0x7fc07fc,0x7fc07fc,
3114  0xe73800,0x380e380e,0x380e380e,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x38dc3838,0x38383838,0x38381c70,
3115  0x381c1c70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xc60,0xf83878,0x71e30000,0x70000e0,0x1007ffe,
3116  0x7f0,0x380,0x381c01c0,0x1e0003c,0x60e0001c,0x381c01c0,0x381c079c,0x0,0x7c000000,0x7c0380,0x63031c1c,0x70307000,0x70387000,
3117  0x7000701c,0x703801c0,0x7071c0,0x7000739c,0x71b8701c,0x7000701c,0x71e00078,0x1c07038,0xe703e7c,0x7e001c0,0xf000380,0x38001c0,
3118  0x0,0x1ffc,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fc0,0x380739c,0x3838381c,0x381c381c,0x7000ff0,0x7003838,0x1ef03bdc,
3119  0x3800ee0,0x1e01f00,0x180007c,0x61fc0000,0x7fc07f3c,0x1c0,0x0,0x1000100,0x1800,0x780c00,0x70707f3c,0xf007f3c,0x7f3c0000,0x0,
3120  0x3c0,0x3ffcffff,0x0,0xff00030,0xe1fe7f3c,0x1e001c0,0x1c0,0x1c200700,0xc183ffe,0xe0c,0x9801,0x1ff038e0,0xc07f0,0x8c610000,
3121  0x180,0x0,0x3838,0x1980380,0x0,0x1ff0071c,0xe000e000,0xe0000f80,0x1c1c1c1c,0x1c1c1c1c,0x1c1c1e38,0x38e07000,0x70007000,0x70007000,
3122  0x1c001c0,0x1c001c0,0x703871b8,0x701c701c,0x701c701c,0x701c03c0,0x761c7038,0x70387038,0x703801c0,0x70703870,0x1ffc1ffc,0x1ffc1ffc,
3123  0x1ffc1ffc,0xfff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c7ffc,0x389c3838,0x38383838,
3124  0x38380ee0,0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0xfffc,0xbc60fc,0x70e30000,0x70000e0,
3125  0x180,0x7f0,0x700,0x381c01c0,0x3e0001c,0x7ffc001c,0x381c03c0,0x381c001c,0x0,0x1f807ffc,0x3f00380,0x63031ffc,0x70387000,0x70387000,
3126  0x7000701c,0x703801c0,0x7071e0,0x7000701c,0x71b8701c,0x7000701c,0x70f00038,0x1c07038,0x7e03e7c,0x77001c0,0xe000380,0x1c001c0,
3127  0x0,0x3c1c,0x381c3800,0x381c3ffe,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x70003f8,0x7003838,0xee03bdc,
3128  0x3c00ee0,0x3c00380,0x18000e0,0xf00000,0x1c007e7c,0x3c0,0x0,0x1000100,0x0,0x381800,0x70707e7c,0xe007e7c,0x7e7c0000,0x0,0x7c0,
3129  0x0,0x0,0x3f80018,0xe1fe7e7c,0x3c001c0,0x1c0,0x1c200700,0xc183ffe,0xf0c,0x8c01,0x38e0,0xc07f0,0x8c710000,0x180,0x0,0x3838,
3130  0x1980000,0x0,0x71c,0x7000f0,0x700f00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x3fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
3131  0x703871b8,0x701c701c,0x701c701c,0x701c07e0,0x7c1c7038,0x70387038,0x703801c0,0x7ff03838,0x3c1c3c1c,0x3c1c3c1c,0x3c1c3c1c,
3132  0x3fff3800,0x3ffe3ffe,0x3ffe3ffe,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x391c3838,0x38383838,0x38380ee0,
3133  0x381c0ee0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfffc,0x9c01ce,0x70f60000,0x70000e0,0x180,
3134  0x0,0x700,0x381c01c0,0x780001c,0x7ffc001c,0x381c0380,0x381c003c,0x0,0x3e07ffc,0xf800380,0x63031ffc,0x70387000,0x70387000,
3135  0x7000701c,0x703801c0,0x7070f0,0x7000701c,0x71b8701c,0x7000701c,0x70700038,0x1c07038,0x7e03e7c,0xf7801c0,0x1e000380,0x1c001c0,
3136  0x0,0x381c,0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01fe0,0x380739c,0x3838381c,0x381c381c,0x7000078,0x7003838,0xee03a5c,
3137  0x7c00fe0,0x78001c0,0x18001c0,0x0,0x1c003ef8,0x380,0x0,0x1000100,0x810,0x383000,0x70703ef8,0x1e003ef8,0x3ef80000,0x0,0x7c0,
3138  0x0,0x0,0x78000c,0xe1c03ef8,0x78001c0,0x1c0,0x1c200700,0x63001c0,0x18003f8,0x4e12,0x1c70,0xc0000,0x4c320000,0x180,0x0,0x3838,
3139  0x1980000,0x0,0xe38,0x700118,0x701e00,0x1ffc1ffc,0x1ffc1ffc,0x1ffc1ffc,0x7fe07000,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
3140  0x703871b8,0x701c701c,0x701c701c,0x701c0e70,0x7c1c7038,0x70387038,0x703801c0,0x7fc0381c,0x381c381c,0x381c381c,0x381c381c,
3141  0x78e03800,0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0000,0x3b1c3838,0x38383838,0x38380fe0,
3142  0x381c0fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1860,0x9c0186,0x707e0000,0x30000c0,0x180,
3143  0x0,0xe00,0x183801c0,0xf00001c,0xe0001c,0x181c0380,0x381c0038,0x0,0xfc0000,0x7e000000,0x61873c1e,0x70383800,0x70707000,0x7000381c,
3144  0x703801c0,0x707070,0x7000701c,0x70f83838,0x70003838,0x70780038,0x1c07038,0x7e03c3c,0xe3801c0,0x1c000380,0xe001c0,0x0,0x381c,
3145  0x381c3800,0x381c3800,0x380381c,0x383801c0,0xe01ef0,0x380739c,0x3838381c,0x381c381c,0x7000038,0x7003838,0xfe03e7c,0xfe007c0,
3146  0x70001c0,0x18001c0,0x0,0xe001ff0,0x380,0x0,0x1000100,0x162c,0x381800,0x30701ff0,0x1c001ff0,0x1ff00000,0x0,0x3c0,0x0,0x0,
3147  0x380018,0xe1c01ff0,0x70001c0,0x1c0,0x1c200700,0xff801c0,0x18000f0,0x63e6,0xe38,0x0,0x6c3e0000,0x0,0x0,0x3838,0x1980000,0x0,
3148  0x1c70,0xf0000c,0xf01c00,0x3c1e3c1e,0x3c1e3c1e,0x3c1e3c1c,0x70e03800,0x70007000,0x70007000,0x1c001c0,0x1c001c0,0x707070f8,
3149  0x38383838,0x38383838,0x38381c38,0x38387038,0x70387038,0x703801c0,0x7000381c,0x381c381c,0x381c381c,0x381c381c,0x70e03800,
3150  0x38003800,0x38003800,0x1c001c0,0x1c001c0,0x381c3838,0x381c381c,0x381c381c,0x381c0380,0x3e1c3838,0x38383838,0x383807c0,0x381c07c0,
3151  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x18c0,0x9c0186,0x783c0000,0x38001c0,0x180,0x3800000,
3152  0x3800e00,0x1c3801c0,0x1e00003c,0xe00038,0x1c1c0780,0x381c0038,0x3800380,0x3c0000,0x78000000,0x61ff380e,0x70383808,0x70707000,
3153  0x7000381c,0x703801c0,0x40707078,0x7000701c,0x70f83838,0x70003838,0x70384038,0x1c07038,0x7e03c3c,0x1e3c01c0,0x3c000380,0xe001c0,
3154  0x0,0x383c,0x3c381c00,0x1c3c1c00,0x3801c3c,0x383801c0,0xe01c78,0x380739c,0x38381c38,0x3c381c3c,0x7000038,0x7003878,0x7c01e78,
3155  0x1ef007c0,0xf0001c0,0x18001c0,0x0,0xe000ee0,0x7800380,0xe380000,0x1001ff0,0x2242,0x40380c00,0x38700ee0,0x3c000ee0,0xee00000,
3156  0x0,0x0,0x0,0x0,0x380030,0xe1c00ee0,0xf0001c0,0x1c0,0xe200700,0xdd801c0,0x1800038,0x300c,0x71c,0x0,0x300c0000,0x0,0x0,0x3838,
3157  0x1980000,0x0,0x38e0,0xb0000c,0xb01c08,0x380e380e,0x380e380e,0x380e380e,0x70e03808,0x70007000,0x70007000,0x1c001c0,0x1c001c0,
3158  0x707070f8,0x38383838,0x38383838,0x3838381c,0x38387038,0x70387038,0x703801c0,0x7000381c,0x383c383c,0x383c383c,0x383c383c,
3159  0x70e01c00,0x1c001c00,0x1c001c00,0x1c001c0,0x1c001c0,0x1c383838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383878,0x38783878,0x387807c0,
3160  0x3c3807c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x18c0,0x10b801ce,0x3c3e0000,0x38001c0,0x180,
3161  0x3800000,0x3801c00,0x1e7801c0,0x3c002078,0xe02078,0x1c380700,0x1c3810f0,0x3800380,0x40000,0x40000380,0x307b380e,0x70701e18,
3162  0x70e07000,0x70001c1c,0x703801c0,0x60e0703c,0x7000701c,0x70f83c78,0x70003c70,0x703c70f0,0x1c03870,0x3c01c3c,0x3c1c01c0,0x78000380,
3163  0x7001c0,0x0,0x3c7c,0x3c381e18,0x1c7c1e0c,0x3801c3c,0x383801c0,0xe01c38,0x3c0739c,0x38381c38,0x3c381c3c,0x7001078,0x7803c78,
3164  0x7c01c38,0x1c780380,0x1e0001c0,0x18001c0,0x0,0x70c06c0,0x7000380,0xe300000,0x1000100,0x2142,0x70f00600,0x3c7006c0,0x780006c0,
3165  0x6c00000,0x0,0x0,0x0,0x0,0x10780060,0x73e206c0,0x1e0001c0,0x1c0,0x7240700,0x180c01c0,0x1800018,0x1818,0x30c,0x0,0x18180000,
3166  0x0,0x0,0x3c78,0x1980000,0x0,0x30c0,0x130000c,0x1301c18,0x380e380e,0x380e380e,0x380e380e,0x70e01e18,0x70007000,0x70007000,
3167  0x1c001c0,0x1c001c0,0x70e070f8,0x3c783c78,0x3c783c78,0x3c781008,0x7c783870,0x38703870,0x387001c0,0x70003a3c,0x3c7c3c7c,0x3c7c3c7c,
3168  0x3c7c3c7c,0x79f11e18,0x1e0c1e0c,0x1e0c1e0c,0x1c001c0,0x1c001c0,0x1c783838,0x1c381c38,0x1c381c38,0x1c380380,0x1c383c78,0x3c783c78,
3169  0x3c780380,0x3c380380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x38c0,0x1ff800fc,0x1fee0000,
3170  0x1800180,0x180,0x3800000,0x3801c00,0xff01ffc,0x3ffc3ff0,0xe03ff0,0xff00700,0x1ff81fe0,0x3800380,0x0,0x380,0x3000780f,0x7ff00ff8,
3171  0x7fc07ff8,0x70000ffc,0x70381ffc,0x7fe0701c,0x7ff8701c,0x70781ff0,0x70001ff0,0x701c7ff0,0x1c01fe0,0x3c01c38,0x380e01c0,0x7ffc0380,
3172  0x7001c0,0x0,0x1fdc,0x3ff00ff0,0xffc0ffc,0x3800fdc,0x38383ffe,0xe01c3c,0x1fc739c,0x38380ff0,0x3ff00ffc,0x7001ff0,0x3f81fb8,
3173  0x7c01c38,0x3c3c0380,0x1ffc01c0,0x18001c0,0x0,0x3fc0380,0x7000380,0xc70718c,0x1000100,0x2244,0x7ff00200,0x1fff0380,0x7ffc0380,
3174  0x3800000,0x0,0x0,0x0,0x0,0x1ff000c0,0x7f7e0380,0x1ffc01c0,0x1c0,0x3fc3ffe,0x1c0,0x1800018,0x7e0,0x104,0x0,0x7e00000,0x7ffe,
3175  0x0,0x3fde,0x1980000,0x0,0x2080,0x3300018,0x3300ff0,0x780f780f,0x780f780f,0x780f780e,0xf0fe0ff8,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,
3176  0x1ffc1ffc,0x7fc07078,0x1ff01ff0,0x1ff01ff0,0x1ff00000,0x7ff01fe0,0x1fe01fe0,0x1fe001c0,0x70003bf8,0x1fdc1fdc,0x1fdc1fdc,
3177  0x1fdc1fdc,0x3fbf0ff0,0xffc0ffc,0xffc0ffc,0x3ffe3ffe,0x3ffe3ffe,0xff03838,0xff00ff0,0xff00ff0,0xff00000,0x3ff01fb8,0x1fb81fb8,
3178  0x1fb80380,0x3ff00380,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1c0,0x31c0,0x7e00078,0x7cf0000,0x1800180,
3179  0x0,0x3800000,0x3803800,0x3c01ffc,0x3ffc0fe0,0xe01fc0,0x3e00e00,0x7e00f80,0x3800380,0x0,0x380,0x18007007,0x7fc003f0,0x7f007ff8,
3180  0x700003f0,0x70381ffc,0x3f80701e,0x7ff8701c,0x707807c0,0x700007c0,0x701e1fc0,0x1c00fc0,0x3c01818,0x780f01c0,0x7ffc0380,0x3801c0,
3181  0x0,0xf9c,0x39e003e0,0x79c03f0,0x380079c,0x38383ffe,0xe01c1e,0x7c739c,0x383807e0,0x39e0079c,0x7000fc0,0x1f80f38,0x3801c38,
3182  0x781e0380,0x1ffc01c0,0x18001c0,0x0,0x1f80100,0xe000700,0x1c60718c,0x1000100,0x1e3c,0x1fc00100,0x7ff0100,0x7ffc0100,0x1000000,
3183  0x0,0x0,0x0,0x0,0xfc00080,0x3e3c0100,0x1ffc01c0,0x1c0,0xf83ffe,0x1c0,0x1800838,0x0,0x0,0x0,0x0,0x7ffe,0x0,0x3b9e,0x1980000,
3184  0x0,0x0,0x2300038,0x23003e0,0x70077007,0x70077007,0x70077007,0xe0fe03f0,0x7ff87ff8,0x7ff87ff8,0x1ffc1ffc,0x1ffc1ffc,0x7f007078,
3185  0x7c007c0,0x7c007c0,0x7c00000,0xc7c00fc0,0xfc00fc0,0xfc001c0,0x700039f0,0xf9c0f9c,0xf9c0f9c,0xf9c0f9c,0x1f1e03e0,0x3f003f0,
3186  0x3f003f0,0x3ffe3ffe,0x3ffe3ffe,0x7e03838,0x7e007e0,0x7e007e0,0x7e00000,0x63e00f38,0xf380f38,0xf380380,0x39e00380,0x0,0x0,
3187  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x3000000,0x3800,0x0,0x0,0x0,0x0,
3188  0x0,0x300,0x0,0x0,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe0,0x0,0x0,0x0,0x0,0x380,0x3801c0,0x0,0x0,0x0,0x0,0x1c,0x0,0xe00000,
3189  0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1c0,0x18001c0,0x0,0x0,0xe000700,0x18600000,0x1000100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3190  0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800ff0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0x1800000,0x0,0x6300070,0x6300000,0x0,
3191  0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xc0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x40000000,
3192  0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,0xc00300,0x0,0x7000000,
3193  0x7000,0x0,0x0,0x0,0x0,0x0,0x700,0x0,0x0,0xf040000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x3f0,0x1c0fc0,0x0,0x0,
3194  0x0,0x0,0x1c,0x0,0xe00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x700,0x1e0,0x18003c0,0x0,0x0,0xc000700,0x18c00000,0x1000000,0x0,
3195  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x18007e0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000,
3196  0x0,0x7f800e0,0x7f80000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,
3197  0x0,0x0,0x0,0x0,0x0,0x0,0x700,0x38000700,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,
3198  0x0,0x600600,0x0,0x6000000,0x0,0x0,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x7fc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,
3199  0x3f0,0xfc0,0x0,0x0,0x0,0x0,0x838,0x0,0x1e00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0xf00,0xfc,0x1801f80,0x0,0x0,0x8008e00,0x30c00000,
3200  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x1980000,0xc00000,
3201  0x0,0x3001c0,0x300000,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x60,0x0,0x0,0x0,0x0,0x0,
3202  0x0,0x0,0x0,0x0,0x0,0xf00,0x38000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x800000,0x0,
3203  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3204  0x0,0x0,0xff0,0x0,0x1fc00000,0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3e00,0x7c,0x1801f00,0x0,0x0,0x800fe00,0x0,0x0,0x0,0x0,0x0,0x0,
3205  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200000,0x0,0x1800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7c00000,0x0,0x3001fc,0x300000,
3206  0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3207  0x3e00,0x38003e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3208  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x7e0,0x0,0x1f000000,
3209  0x0,0x0,0x3800001c,0x0,0x0,0x0,0x3c00,0x0,0x1800000,0x0,0x0,0x7800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3210  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3800,0x0,0x7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3211  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00,0x38003c00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3212  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3213  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1800000,0x0,0x0,0x0,0x0,
3214  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3215  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3216  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3217  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3218  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3219  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3220  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3221  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3222  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3223  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3224  0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
3225 
3226  // Define a 29x57 font (extra large size).
3227  const unsigned int font29x57[29*57*256/32] = {
3228  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3229  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3230  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3231  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3232  0x0,0x781e00,0x0,0x0,0x7,0x81e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3233  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0000,0xf8000,0x7e00000,0x0,0x7,
3234  0xc0000000,0x0,0x7c00,0xf80,0x7e000,0x0,0x7c00000,0xf80000,0x7e000000,0x0,0x0,0x1f00,0x3e0,0x1f800,0x0,0x0,0x0,0x3,0xe0000000,
3235  0x7c00003f,0x0,0xf8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3236  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3237  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3238  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3239  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3240  0x0,0x0,0x0,0x0,0x0,0x0,0x3c3c00,0x0,0x0,0x3,0xc3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,
3241  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e0000,
3242  0x1f0000,0x7e00000,0xf838001f,0xf80001f,0xf0000000,0x0,0x3e00,0x1f00,0x7e000,0x3e1f000,0x3e00000,0x1f00000,0x7e00003e,0x1f000000,
3243  0x3e0,0xe0000f80,0x7c0,0x1f800,0x3e0e00,0x7c3e000,0x0,0x1,0xf0000000,0xf800003f,0x1f0f,0x800001f0,0x0,0x0,0x0,0x0,0x0,0x0,
3244  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3245  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3246  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3247  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3248  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e7800,0x0,0x0,
3249  0x1,0xe7800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3250  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x1e0000,0xff00001,0xfe38001f,0xf80003f,
3251  0xf8000000,0x0,0x1e00,0x1e00,0xff000,0x3e1f000,0x1e00000,0x1e00000,0xff00003e,0x1f000000,0x7f8,0xe0000780,0x780,0x3fc00,0x7f8e00,
3252  0x7c3e000,0x0,0x0,0xf0000000,0xf000007f,0x80001f0f,0x800001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3253  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3254  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3255  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3256  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3257  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xef000,0x0,0x0,0x0,0xef000000,0x0,0x0,0x0,0x0,0x0,0x0,
3258  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3259  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000,0x3c0000,0x1e780003,0xfff8001f,0xf80003c,0x78000000,0x0,0xf00,0x3c00,0x1e7800,
3260  0x3e1f000,0xf00000,0x3c00001,0xe780003e,0x1f000000,0xfff,0xe00003c0,0xf00,0x79e00,0xfffe00,0x7c3e000,0x0,0x0,0x78000001,0xe00000f3,
3261  0xc0001f0f,0x800003c0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3262  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3263  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3264  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3265  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3266  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3267  0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3268  0x0,0x78000,0x780000,0x3c3c0003,0x8ff0001f,0xf800078,0x3c000000,0x0,0x780,0x7800,0x3c3c00,0x3e1f000,0x780000,0x7800003,0xc3c0003e,
3269  0x1f000000,0xe3f,0xc00001e0,0x1e00,0xf0f00,0xe3fc00,0x7c3e000,0x0,0x0,0x3c000003,0xc00001e1,0xe0001f0f,0x80000780,0x0,0x0,
3270  0x0,0x0,0x0,0x0,0x1f,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3271  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3272  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3273  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3274  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3275  0x0,0x7e000,0x0,0x0,0x0,0x7e000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,
3276  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc00,0x7e000,0xfe000,0x0,0x3c000,0xf00000,0x781e0003,
3277  0x83e0001f,0xf800070,0x1c000000,0x0,0x3c0,0xf000,0x781e00,0x3e1f000,0x3c0000,0xf000007,0x81e0003e,0x1f000000,0xe0f,0x800000f0,
3278  0x3c00,0x1e0780,0xe0f800,0x7c3e000,0x0,0x0,0x1e000007,0x800003c0,0xf0001f0f,0x80000f00,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf8000000,
3279  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3280  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3281  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3282  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3283  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3284  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3285  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ff800,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3286  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3287  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3288  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3289  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3290  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3291  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3292  0x0,0x0,0x78,0xf000000,0x0,0x0,0x780f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c0,
3293  0x0,0x0,0x0,0x0,0x0,0x0,0x3fc00,0x1fe000,0x3ffc00,0x0,0x0,0x0,0x0,0x0,0x70,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3294  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00000,0x3e000,0x3e00000,0x0,0x78,0x3c000000,0x0,0x1f000,0x3e0,
3295  0x3e000,0x0,0x1f000000,0x3e0000,0x3e000000,0x0,0x0,0x7c00,0xf8,0xf800,0x0,0x0,0x0,0xf,0x80000000,0x1f00001f,0x0,0x3e,0x0,
3296  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3297  0x0,0x0,0x0,0x30000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3298  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80000,
3299  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3300  0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x781c0000,0x38,0xe000000,0x0,0x0,0x380e0,0x0,
3301  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x0,0x0,0x0,0x0,0x0,0x0,0x39c00,0x1ce000,0x303e00,
3302  0x0,0x0,0x0,0x0,0x0,0x78,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,
3303  0x0,0x0,0xf80000,0x7c000,0x3e00000,0xf0380000,0x70,0x1c000000,0x0,0xf800,0x7c0,0x3e000,0x0,0xf800000,0x7c0000,0x3e000000,
3304  0x0,0x3c0,0xe0003e00,0x1f0,0xf800,0x3c0e00,0x0,0x0,0x7,0xc0000000,0x3e00001f,0x0,0x7c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3305  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0xff,0x0,
3306  0xf8,0xf8000,0x1c000,0x0,0x0,0x0,0x0,0x1f,0xc0000000,0x1ff8,0xff00,0x0,0x0,0x3fe000,0x0,0x1fc00001,0xfe000000,0x0,0x0,0x0,
3307  0x0,0x7f800,0x0,0x0,0x0,0xff00000,0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf8000000,0xfe,0x0,0x7f80,0x0,0x0,0x0,0x0,0x0,
3308  0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x780000,0x1,0xe0000000,0x0,0x780000,0x3,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,
3309  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x3fc00,0x0,0x0,0x1fc000,0x0,0x0,0x0,0x1fc0,
3310  0x0,0xff000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xfe1c0000,0x1c,0x1c000000,0x0,0x0,0x1c1c0,0x0,0x0,0x0,0x0,0x1fe0000,
3311  0x0,0x0,0x1ff,0x1f0f8,0x0,0xff000,0x0,0x0,0x0,0x3f,0xff00000f,0x80000000,0xfe0,0x3f80,0xf00,0x0,0x0,0x0,0x1,0xf8000003,0xe0000000,
3312  0x1c00,0xe000,0xe00,0x0,0x0,0x0,0x0,0x0,0x3c,0x78000000,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,
3313  0x7f0000,0x0,0x1fc07000,0x0,0x0,0x0,0x0,0x0,0x3f800,0x780000,0x78000,0x7f00001,0xfc38001f,0xf800070,0x1c000000,0x0,0x7800,
3314  0x780,0x7f000,0x3e1f000,0x7800000,0x780000,0x7f00003e,0x1f0003f0,0x7f0,0xe0001e00,0x1e0,0x1fc00,0x7f0e00,0x7c3e000,0x0,0x3,
3315  0xc0000000,0x3c00003f,0x80001f0f,0x80000078,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3316  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x1e078000,0x30000000,0x3ff,0xc00001e0,0xf0,
3317  0x78000,0x1c000,0x0,0x0,0x0,0x0,0x1e0007f,0xf000007e,0x1ffff,0x7ffe0,0x1f80,0x3ffff80,0xfff803,0xfffff800,0xfff80007,0xff800000,
3318  0x0,0x0,0x0,0x0,0x1ffe00,0x0,0xfe0003,0xfff80000,0x3ffe01ff,0xe00003ff,0xffe01fff,0xff0003ff,0xe01e0007,0x803ffff0,0xfff80,
3319  0x3c000fc0,0x7800001f,0x8003f07e,0x1e000f,0xfe0007ff,0xf00003ff,0x8007ffe0,0x1fff8,0x7fffffe,0xf0003c1,0xe000079e,0xf1f,0x1f3e0,
3320  0x1f01ff,0xfff8003f,0xf003c000,0x7fe0,0x3f00,0x0,0x3c0000,0x1,0xe0000000,0x0,0x780000,0xf,0xfe000000,0x78000,0x3c00,0xf000,
3321  0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xfc0000f0,0x3fe00,0x0,0x0,0xfff00,0x0,0x0,0x3fe000,
3322  0x0,0x0,0x0,0x1dc0,0x0,0x3fff00,0x0,0x3ffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff1c07ff,0x3c0f001e,0x3c000000,
3323  0x0,0x0,0x1e3c0,0xf80007c,0x0,0x780000,0x0,0xfff8000,0x3e00,0x1f00000,0x7ff,0xc001f0f8,0x0,0x3ffc00,0x0,0x0,0x0,0x3f,0xff00003f,
3324  0xe0000000,0x3ff8,0xffe0,0x1e00,0x0,0xfffc00,0x0,0x7,0xf800000f,0xf8000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,
3325  0x3f800001,0xfc00003f,0xf80000ff,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,
3326  0xfc00,0x3c001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x0,0x7ff8f0f0,0x3c0780,0x1e03c00,0xf01e000,0x783e0001,0xf01e0000,0xffe00,
3327  0x3c0000,0xf0000,0x7700001,0xfe38001f,0xf800070,0x1c000000,0x0,0x3c00,0xf00,0x77000,0x3e1f000,0x3c00000,0xf00000,0x7700003e,
3328  0x1f0000f8,0xc0007f8,0xe0000f00,0x3c0,0x1dc00,0x7f8e00,0x7c3e000,0x0,0x1,0xe0000000,0x7800003b,0x80001f0f,0x800000f0,0x1e0000,
3329  0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3330  0x0,0x0,0x780000,0x3c1e0000,0x1e070000,0x300001f0,0x7ff,0xc00001e0,0x1e0,0x7c000,0x1c000,0x0,0x0,0x0,0x0,0x3c000ff,0xf80007fe,
3331  0x3ffff,0x801ffff8,0x1f80,0x3ffff80,0x3fff803,0xfffff801,0xfffc000f,0xffc00000,0x0,0x0,0x0,0x0,0x7fff80,0x0,0xfe0003,0xffff0000,
3332  0xffff01ff,0xfc0003ff,0xffe01fff,0xff000fff,0xf01e0007,0x803ffff0,0xfff80,0x3c001f80,0x7800001f,0xc007f07e,0x1e001f,0xff0007ff,
3333  0xfc0007ff,0xc007fffc,0x3fffc,0x7fffffe,0xf0003c1,0xf0000f9e,0xf0f,0x8003e1e0,0x1e01ff,0xfff8003f,0xf001e000,0x7fe0,0x3f00,
3334  0x0,0x1e0000,0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,
3335  0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x1fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x3de0,0x0,0x7fff80,0x0,0xfffff80,
3336  0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe7bc07ff,0x3e1f000f,0x78000000,0x0,0x0,0xf780,0x7800078,0x0,0x780000,0x180000,
3337  0x1fff8000,0x1e00,0x1e0003c,0xfff,0xc001f0f8,0x0,0x7ffe00,0x0,0x0,0x0,0x3f,0xff00007f,0xf0000000,0x3ffc,0xfff0,0x3c00,0x0,
3338  0x7fffc00,0x0,0x7,0xf800003f,0xfe000000,0x1c00,0xe000,0xe00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xe00001ff,
3339  0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000fc00,0x3c003ffe,0x1fff0,
3340  0xfff80,0x7ffc00,0x3ffe000,0x0,0xfffce0f0,0x3c0780,0x1e03c00,0xf01e000,0x781e0001,0xe01e0000,0x3fff00,0x1e0000,0x1e0000,0xf780003,
3341  0xcf78001f,0xf800078,0x3c000000,0x0,0x1e00,0x1e00,0xf7800,0x3e1f000,0x1e00000,0x1e00000,0xf780003e,0x1f0000fc,0x7c000f3d,
3342  0xe0000780,0x780,0x3de00,0xf3de00,0x7c3e000,0x0,0x0,0xf0000000,0xf000007b,0xc0001f0f,0x800001e0,0x1e0000,0x3e1f00,0x0,0x0,
3343  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,
3344  0x3c1e0000,0x1e0f0000,0x300007fc,0xfff,0xc00001e0,0x1e0,0x3c000,0x1c000,0x0,0x0,0x0,0x0,0x3c001ff,0xfc001ffe,0x3ffff,0xc01ffffc,
3345  0x3f80,0x3ffff80,0x7fff803,0xfffff803,0xfffe001f,0xffe00000,0x0,0x0,0x0,0x0,0xffff80,0x7f800,0xfe0003,0xffff8001,0xffff01ff,
3346  0xff0003ff,0xffe01fff,0xff001fff,0xf01e0007,0x803ffff0,0xfff80,0x3c003f00,0x7800001f,0xc007f07f,0x1e003f,0xff8007ff,0xff000fff,
3347  0xe007ffff,0x7fffc,0x7fffffe,0xf0003c0,0xf0000f1e,0xf07,0x8003c1f0,0x3e01ff,0xfff8003f,0xf001e000,0x7fe0,0x7f80,0x0,0xe0000,
3348  0x1,0xe0000000,0x0,0x780000,0x1f,0xfe000000,0x78000,0x3c00,0xf000,0x7800003,0xffe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,
3349  0x0,0x0,0x0,0x0,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x3fff80,0x0,0x0,0xffe000,0x0,0x0,0x0,0x78f0,0x0,0xffff80,0x0,0x3fffff80,0x1f,
3350  0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc7f80070,0x3e1f0007,0x70000000,0x0,0x0,0x7700,0x7c000f8,0x0,0x780000,0x180000,
3351  0x3fff8000,0x1f00,0x3e0003c,0x1f03,0xc001f0f8,0x0,0x703f00,0x0,0x0,0x0,0x3f,0xff0000f0,0xf8000000,0x303e,0xc0f8,0x7800,0x0,
3352  0xffffc00,0x0,0x7,0x3800003e,0x3e000000,0x1c00,0xe000,0x3c00,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00000f,0xe00001ff,
3353  0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000fe00,0x3c007fff,0x3fff8,
3354  0x1fffc0,0xfffe00,0x7fff000,0x1,0xffffc0f0,0x3c0780,0x1e03c00,0xf01e000,0x781f0003,0xe01e0000,0x3fff80,0xe0000,0x3c0000,0x1e3c0003,
3355  0x8ff0001f,0xf80003c,0x78000000,0x0,0xe00,0x3c00,0x1e3c00,0x3e1f000,0xe00000,0x3c00001,0xe3c0003e,0x1f00007f,0xf8000e3f,0xc0000380,
3356  0xf00,0x78f00,0xe3fc00,0x7c3e000,0x0,0x0,0x70000001,0xe00000f1,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,
3357  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0000,
3358  0x30000ffe,0xf80,0xc00001e0,0x3c0,0x1e000,0x101c040,0x0,0x0,0x0,0x0,0x78003f0,0x7e001ffe,0x3f807,0xe01f00fe,0x3f80,0x3ffff80,
3359  0x7e01803,0xfffff007,0xe03f003f,0x3f00000,0x0,0x0,0x0,0x0,0xfc0fc0,0x3ffe00,0xfe0003,0xffffc003,0xf81f01ff,0xff8003ff,0xffe01fff,
3360  0xff003f01,0xf01e0007,0x803ffff0,0xfff80,0x3c007e00,0x7800001f,0xc007f07f,0x1e007e,0xfc007ff,0xff801f83,0xf007ffff,0x800fc07c,
3361  0x7fffffe,0xf0003c0,0xf0000f0f,0x1e07,0xc007c0f8,0x7c01ff,0xfff8003c,0xf000,0x1e0,0xffc0,0x0,0xf0000,0x1,0xe0000000,0x0,0x780000,
3362  0x3e,0x0,0x78000,0x3c00,0xf000,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0x800000f0,0x1f80,
3363  0x0,0x0,0x7e0780,0x0,0x0,0x1f82000,0x0,0x0,0x0,0x7070,0x0,0x1f80f80,0x0,0x7fffff80,0x1f,0xffff8000,0x0,0x0,0x0,0x0,0x0,0x0,
3364  0x0,0x1,0xc3f80070,0x3f3f0007,0xf0000000,0x0,0x0,0x7f00,0x3e001f0,0x0,0x780000,0x180000,0x7f018000,0xf80,0x7c0003c,0x3e00,
3365  0x4001f0f8,0xfe00,0x400f00,0x0,0x0,0x0,0x7f000000,0xe0,0x38000000,0x1e,0x38,0x7800,0x0,0x1ffe1c00,0x0,0x0,0x38000078,0xf000000,
3366  0x1c00,0xe000,0x7f800,0xf000,0x1fc000,0xfe0000,0x7f00000,0x3f800001,0xfc00001f,0xf00001ff,0xffc03f81,0xf007ffff,0xc03ffffe,
3367  0x1fffff0,0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf800fe00,0x3c00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,
3368  0x3,0xf07fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x780f8007,0xc01e0000,0x7e0fc0,0xf0000,0x3c0000,0x1c1c0003,0x87f0001f,0xf80003f,
3369  0xf8000000,0x0,0xf00,0x3c00,0x1c1c00,0x3e1f000,0xf00000,0x3c00001,0xc1c0003e,0x1f00003f,0xc0000e1f,0xc00003c0,0xf00,0x70700,
3370  0xe1fc00,0x7c3e000,0x0,0x0,0x78000001,0xe00000e0,0xe0001f0f,0x800003c0,0x1e0000,0x3e1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3371  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c0f0001,0xff801e0f,
3372  0x1f00,0x1e0,0x3c0,0x1e000,0x3c1c1e0,0x0,0x0,0x0,0x0,0x78007c0,0x1f001f9e,0x3c001,0xf010003e,0x7780,0x3c00000,0xf800000,0xf007,
3373  0xc01f007c,0x1f80000,0x0,0x0,0x0,0x0,0xe003e0,0x7fff00,0x1ef0003,0xc007e007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x301e0007,
3374  0x80007800,0x780,0x3c00fc00,0x7800001f,0xe00ff07f,0x1e00f8,0x3e00780,0x1fc03e00,0xf807801f,0xc01f001c,0xf000,0xf0003c0,0xf0000f0f,
3375  0x1e03,0xc00f8078,0x780000,0xf0003c,0xf000,0x1e0,0x1f3e0,0x0,0x78000,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,
3376  0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1f,0xf0,0xf80,0x0,0x0,0xf80180,0x0,0x0,0x1e00000,
3377  0x0,0x0,0x0,0xe038,0x0,0x3e00380,0x0,0xfe0f0000,0x0,0xf0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xc0f00070,0x3b370003,0xe0000000,
3378  0x0,0x0,0x3e00,0x1e001e0,0x0,0x780000,0x180000,0x7c000000,0x780,0x780003c,0x3c00,0x0,0x7ffc0,0x780,0x0,0x0,0x3,0xffe00000,
3379  0x1c0,0x3c000000,0xe,0x38,0xf000,0x0,0x3ffe1c00,0x0,0x0,0x38000078,0xf000000,0x1c00,0xe000,0x7f000,0xf000,0x3de000,0x1ef0000,
3380  0xf780000,0x7bc00003,0xde00001e,0xf00003e7,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3381  0xe0001e03,0xfc00fe00,0x3c01f007,0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x7,0xc01f80f0,0x3c0780,0x1e03c00,0xf01e000,0x78078007,
3382  0x801e0000,0x7803c0,0x78000,0x780000,0x380e0003,0x81e00000,0x1f,0xf0000000,0x0,0x780,0x7800,0x380e00,0x0,0x780000,0x7800003,
3383  0x80e00000,0x1ff,0x80000e07,0x800001e0,0x1e00,0xe0380,0xe07800,0x0,0x0,0x0,0x3c000003,0xc00001c0,0x70000000,0x780,0x1e0000,
3384  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3385  0x780000,0x3c1e0000,0x3c0e0007,0xfff01c07,0x1e00,0x1e0,0x780,0xf000,0x3e1c3e0,0x0,0x0,0x0,0x0,0xf0007c0,0x1f00181e,0x20000,
3386  0xf000001f,0xf780,0x3c00000,0x1f000000,0x1f00f,0x800f8078,0xf80000,0x0,0x0,0x0,0x0,0x8003e0,0x1fc0f80,0x1ef0003,0xc001e007,
3387  0x800101e0,0x7e003c0,0x1e00,0x7800,0x101e0007,0x80007800,0x780,0x3c00f800,0x7800001e,0xe00ef07f,0x801e00f0,0x1e00780,0x7c03c00,
3388  0x78078007,0xc01e0004,0xf000,0xf0003c0,0x78001e0f,0x1e03,0xe00f807c,0xf80000,0x1f0003c,0x7800,0x1e0,0x3e1f0,0x0,0x3c000,0x1,
3389  0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,
3390  0x1e,0xf0,0x780,0x0,0x0,0x1f00080,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x1e03c,0x0,0x3c00080,0x0,0xf80f0000,0x0,0x1f0000,0x0,0x0,
3391  0x0,0x0,0x0,0x0,0x0,0x0,0x70,0x3bf70003,0xe0000000,0x0,0x0,0x3e00,0x1f003e0,0x0,0x780000,0x180000,0x78000000,0x7c0,0xf80003c,
3392  0x3c00,0x0,0x1f01f0,0x780,0x0,0x0,0xf,0x80f80000,0x1c0,0x1c000000,0xe,0x38,0x1e000,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,0x7800000,
3393  0x1c00,0xe000,0x7fc00,0xf000,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x80007800,0x10078000,0x3c0000,
3394  0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00ff00,0x3c01e003,0xc00f001e,0x7800f0,0x3c00780,0x1e003c00,
3395  0x7,0x800f00f0,0x3c0780,0x1e03c00,0xf01e000,0x7807c00f,0x801e0000,0xf803c0,0x3c000,0xf00000,0x780f0000,0x0,0x7,0xc0000000,
3396  0x0,0x3c0,0xf000,0x780f00,0x0,0x3c0000,0xf000007,0x80f00000,0x7ff,0xc0000000,0xf0,0x3c00,0x1e03c0,0x0,0x0,0x0,0x0,0x1e000007,
3397  0x800003c0,0x78000000,0xf00,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3398  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x3c1e001f,0xfff03803,0x80001e00,0x1e0,0x780,0xf000,0xf9cf80,
3399  0x0,0x0,0x0,0x0,0xf000780,0xf00001e,0x0,0xf800000f,0xe780,0x3c00000,0x1e000000,0x1e00f,0x78078,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,
3400  0x3f003c0,0x1ef0003,0xc000f00f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,0x3c01f000,0x7800001e,0xe00ef07f,
3401  0x801e01f0,0x1e00780,0x3c07c00,0x78078003,0xc03e0000,0xf000,0xf0003c0,0x78001e0f,0x1e01,0xf01f003c,0xf00000,0x3e0003c,0x7800,
3402  0x1e0,0x7c0f8,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,
3403  0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x8,0x40,0x0,0x7e0000,0x7c00000,0x1,0xf00f0000,
3404  0x0,0x3e0000,0x0,0x3f,0xfc0,0xfc3f0,0xfc3f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,0xf003c0,0x0,0x0,0x180000,0xf8000000,
3405  0x3c0,0xf00003c,0x3c00,0x0,0x3c0078,0x7ff80,0x0,0x0,0x1e,0x3c0000,0x1c0,0x1c000000,0xe,0xf0,0x0,0x0,0x7ffe1c00,0x0,0x0,0x380000f0,
3406  0x7800000,0x1c00,0xe000,0x3c00,0x0,0x3de000,0x1ef0000,0xf780000,0x7bc00003,0xde00001e,0xf00003c7,0x8000f800,0x78000,0x3c0000,
3407  0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00ff00,0x3c03e003,0xc01f001e,0xf800f0,0x7c00780,0x3e003c00,
3408  0xf,0x800f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803c00f,0x1fffc0,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3409  0x0,0x0,0x307,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1e0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3410  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,0x781e003f,0xfff03803,
3411  0x80001e00,0x1e0,0xf80,0xf000,0x3dde00,0x0,0x0,0x0,0x0,0xf000f00,0x780001e,0x0,0x7800000f,0x1e780,0x3c00000,0x3e000000,0x3e00f,
3412  0x780f0,0x7c0000,0x0,0x0,0x0,0x0,0x1e0,0x7c001e0,0x3ef8003,0xc000f00f,0x1e0,0xf003c0,0x1e00,0xf000,0x1e0007,0x80007800,0x780,
3413  0x3c03e000,0x7800001e,0xf01ef07b,0xc01e01e0,0xf00780,0x3e07800,0x3c078003,0xe03c0000,0xf000,0xf0003c0,0x78001e0f,0x1e00,0xf01e003e,
3414  0x1f00000,0x3c0003c,0x7800,0x1e0,0x78078,0x0,0x0,0x1,0xe0000000,0x0,0x780000,0x3c,0x0,0x78000,0x0,0x0,0x7800000,0x1e00000,
3415  0x0,0x0,0x0,0x0,0x0,0x0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,
3416  0xe70000,0x7800000,0x1,0xe00f0000,0x0,0x3c0000,0x0,0x3f,0xfc0,0xfc1f0,0x1f83f0,0x0,0x0,0x0,0x70,0x39e70000,0x0,0x0,0x0,0x0,
3417  0xf807c0,0x0,0x0,0x180000,0xf0000000,0x3e0,0x1f00003c,0x3e00,0x0,0x70001c,0x3fff80,0x0,0x0,0x38,0xe0000,0x1c0,0x1c000078,
3418  0x1c,0x1fe0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x7df000,0x3ef8000,0x1f7c0000,0xfbe00007,
3419  0xdf00003c,0x780003c7,0x8000f000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f780,
3420  0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0xf80f0,0x3c0780,0x1e03c00,0xf01e000,0x7803e01f,0x1ffff8,0xf001e0,
3421  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0xc000,0x0,0x0,0x0,0x0,0x1e0000,
3422  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3423  0x780000,0x3c1e0000,0x781e003e,0x30703803,0x80001e00,0x1e0,0xf00,0x7800,0xff800,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,
3424  0x0,0x7800000f,0x3c780,0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x2000000,0x800000,0x1e0,0x78000e0,0x3c78003,
3425  0xc000f01e,0x1e0,0xf803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x701cf07b,0xc01e01e0,0xf00780,0x1e07800,
3426  0x3c078001,0xe03c0000,0xf000,0xf0003c0,0x7c003e0f,0x1e00,0xf83e001e,0x1e00000,0x7c0003c,0x3c00,0x1e0,0xf807c,0x0,0x0,0x1fe0001,
3427  0xe1fc0000,0x7f00003,0xf8780007,0xf000003c,0x7f0,0x783f0,0x0,0x0,0x7800000,0x1e00000,0x3e0f8000,0xfc00007,0xf8000007,0xf00001fc,
3428  0xf,0xc0003fc0,0x3c000,0x0,0x0,0x0,0x0,0x0,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,0x0,0x0,0x3c00000,0x0,0x18,0xc0,0x0,0x1818000,
3429  0x7800000,0x1,0xe00f0000,0x0,0x7c0000,0x0,0x1f,0x80001f80,0x7c1f8,0x1f83e0,0x0,0x0,0x0,0x70,0x38c70007,0xf8000000,0x7f03,
3430  0xf0000000,0x0,0x780780,0x0,0x0,0xfe0000,0xf0000000,0x1e0,0x1e00003c,0x3f00,0x0,0xe07f0e,0x7fff80,0x0,0x0,0x70,0x70000,0x1c0,
3431  0x1c000078,0x3c,0x1fc0,0x0,0x0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800000,0x1c00,0xe000,0xe00,0x0,0x78f000,0x3c78000,0x1e3c0000,
3432  0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,
3433  0xf80f780,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0xf,0x1f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801e01e,0x1ffffc,
3434  0xf007e0,0x3fc000,0x1fe0000,0xff00000,0x7f800003,0xfc00001f,0xe0000fc0,0xfc00007f,0xfe0,0x7f00,0x3f800,0x1fc000,0x0,0x0,0x0,
3435  0x1,0xf000001f,0x80000ff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x1f80000,0x1fc1e000,0x0,0x0,0x0,0x0,0x1e1fc0,0x0,0x0,0x0,0x0,
3436  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e0000,
3437  0x781c007c,0x30003803,0x80001f00,0x1e0,0xf00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x1e000f00,0x780001e,0x0,0x7800000f,0x3c780,
3438  0x3c00000,0x3c000000,0x3c00f,0x780f0,0x3c0000,0x0,0x0,0x1e000000,0xf00000,0x3e0,0xf0000e0,0x3c78003,0xc000f01e,0x1e0,0x7803c0,
3439  0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c0f8000,0x7800001e,0x701cf079,0xe01e01e0,0xf00780,0x1e07800,0x3c078001,0xe03c0000,
3440  0xf000,0xf0003c0,0x3c003c0f,0x3e00,0x787c001f,0x3e00000,0xf80003c,0x3c00,0x1e0,0x1f003e,0x0,0x0,0x1fffc001,0xe7ff0000,0x3ffe000f,
3441  0xfe78003f,0xfc001fff,0xfe001ffc,0xf0078ffc,0x1ffc00,0x7ff000,0x7800f80,0x1e0000f,0x7f1fc01e,0x3ff0001f,0xfe00079f,0xfc0007ff,
3442  0x3c003c7f,0xf001fff8,0x1fffff0,0x3c003c0,0xf0000f1e,0xf1f,0x7c1f0,0x1f00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3c00000,0x100000,
3443  0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7800000,0x1,0xe00f0000,0x1000000,0xf80000,0x40000002,0xf,0x80001f00,0x7e0f8,0x1f07c0,
3444  0x0,0x0,0x0,0x70,0x38c7003f,0xff000000,0xff8f,0xf8000100,0xffffe,0x7c0f80,0x0,0x0,0x3ffc000,0xf0000020,0x1001f0,0x3c00003c,
3445  0x1f80,0x0,0x1c3ffc7,0x7c0780,0x0,0x0,0xe3,0xff038000,0xe0,0x38000078,0x78,0x1ff0,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,
3446  0x7800000,0x1c00,0xe000,0xe00,0xf000,0x78f000,0x3c78000,0x1e3c0000,0xf1e00007,0x8f00003c,0x78000787,0x8001e000,0x78000,0x3c0000,
3447  0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,
3448  0x4000200f,0x3f80f0,0x3c0780,0x1e03c00,0xf01e000,0x7801f03e,0x1ffffe,0xf01fe0,0x3fff800,0x1fffc000,0xfffe0007,0xfff0003f,
3449  0xff8001ff,0xfc003ff3,0xfe0003ff,0xe0007ff8,0x3ffc0,0x1ffe00,0xfff000,0x3ff80001,0xffc0000f,0xfe00007f,0xf000003f,0xf8003c7f,
3450  0xe0003ffc,0x1ffe0,0xfff00,0x7ff800,0x3ffc000,0x1f80000,0xfff1c03c,0x3c01e0,0x1e00f00,0xf007800,0x781f0001,0xf01e7ff0,0x7c0007c,
3451  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,
3452  0x3c1e003f,0xfffff078,0x30003803,0x80000f00,0x1e0,0x1f00,0x7800,0x7f000,0x1e0000,0x0,0x0,0x0,0x3c000f00,0x780001e,0x0,0x7800000f,
3453  0x78780,0x3c00000,0x3c000000,0x7c00f,0x780f0,0x3c0007,0xe000003f,0x0,0xfe000000,0xfe0000,0x3c0,0x1f000070,0x7c7c003,0xc000f01e,
3454  0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c1f0000,0x7800001e,0x783cf079,0xe01e03c0,0xf00780,0x1e0f000,0x3c078001,
3455  0xe03c0000,0xf000,0xf0003c0,0x3c003c07,0x81f03c00,0x7c7c000f,0x87c00000,0xf00003c,0x1e00,0x1e0,0x3e001f,0x0,0x0,0x3fffe001,
3456  0xefff8000,0x7fff001f,0xff78007f,0xfe001fff,0xfe003ffe,0xf0079ffe,0x1ffc00,0x7ff000,0x7801f00,0x1e0000f,0xffbfe01e,0x7ff8003f,
3457  0xff0007bf,0xfe000fff,0xbc003cff,0xf803fffc,0x1fffff0,0x3c003c0,0x78001e1e,0xf0f,0x800f80f0,0x1e00ff,0xffe0001e,0xf0,0x780,
3458  0x0,0x0,0x3c00000,0x380000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1008000,0x7800000,0x3,0xe00f0000,0x3800000,0xf00000,0xe0000007,
3459  0xf,0x80001f00,0x3e0f8,0x1e07c0,0x0,0x0,0x0,0x70,0x3807007f,0xff800000,0x1ffdf,0xfc000380,0xffffe,0x3e1f00,0x0,0x0,0xfffe000,
3460  0xf0000030,0x3800f8,0x7c00003c,0xfc0,0x0,0x18780c3,0xf00780,0x80100,0x0,0xc3,0xffc18000,0xf0,0x78000078,0xf0,0xf0,0x0,0x3c003c0,
3461  0xfffe1c00,0x0,0x0,0x380000f0,0x7800801,0x1c00,0xe000,0x1e00,0xf000,0xf8f800,0x7c7c000,0x3e3e0001,0xf1f0000f,0x8f80007c,0x7c000787,
3462  0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078001,0xe03c000f,
3463  0x1e00078,0xf0003c0,0x78001e00,0xe000701f,0x3fc0f0,0x3c0780,0x1e03c00,0xf01e000,0x7800f87c,0x1e007f,0xf07e00,0x7fffc00,0x3fffe001,
3464  0xffff000f,0xfff8007f,0xffc003ff,0xfe007ff7,0xff0007ff,0xf000fffc,0x7ffe0,0x3fff00,0x1fff800,0x3ff80001,0xffc0000f,0xfe00007f,
3465  0xf00000ff,0xf8003cff,0xf0007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x1f80001,0xfffb803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,
3466  0xe01efff8,0x3c00078,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3467  0x0,0x0,0x0,0x0,0x0,0x780000,0x3c1e003f,0xfffff078,0x30001c07,0xf80,0x1e0,0x1e00,0x3c00,0xff800,0x1e0000,0x0,0x0,0x0,0x3c001e00,
3468  0x3c0001e,0x0,0x7800001e,0x70780,0x3c00000,0x78000000,0x78007,0x800f00f0,0x3e0007,0xe000003f,0x3,0xfe000000,0xff8000,0x7c0,
3469  0x1e000070,0x783c003,0xc001f01e,0x1e0,0x7803c0,0x1e00,0x1e000,0x1e0007,0x80007800,0x780,0x3c3e0000,0x7800001e,0x3838f079,
3470  0xe01e03c0,0x780780,0x1e0f000,0x1e078001,0xe03c0000,0xf000,0xf0003c0,0x3c007c07,0x81f03c00,0x3ef80007,0x87800000,0x1f00003c,
3471  0x1e00,0x1e0,0x7c000f,0x80000000,0x0,0x3ffff001,0xffffc000,0xffff003f,0xff7800ff,0xff001fff,0xfe007ffe,0xf007bffe,0x1ffc00,
3472  0x7ff000,0x7803e00,0x1e0000f,0xffffe01e,0xfff8007f,0xff8007ff,0xff001fff,0xbc003dff,0xf807fffc,0x1fffff0,0x3c003c0,0x78001e0f,
3473  0x1e07,0xc01f00f0,0x1e00ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7c00000,0x7c0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1018000,0x7800000,
3474  0x3,0xc00f0000,0x7c00000,0x1f00001,0xf000000f,0x80000007,0xc0003e00,0x1e07c,0x3e0780,0x0,0x0,0x0,0x70,0x380700ff,0xff800000,
3475  0x3ffff,0xfe0007c0,0xffffe,0x1e1e00,0x0,0x780000,0x1fffe000,0xf0000078,0x7c0078,0x7800003c,0xff0,0x0,0x38e0003,0x80f00780,
3476  0x180300,0x0,0x1c3,0x81e1c000,0x7f,0xf0000078,0x1e0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x380000f0,0x7800c01,0x80001c00,
3477  0xe000,0x603e00,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x7800078,0x3c000f87,0x8001e000,0x78000,0x3c0000,0x1e00000,
3478  0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f3c0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f01,0xf000f81e,
3479  0x7bc0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007878,0x1e001f,0xf0f800,0x7fffe00,0x3ffff001,0xffff800f,0xfffc007f,0xffe003ff,
3480  0xff007fff,0xff800fff,0xf001fffe,0xffff0,0x7fff80,0x3fffc00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00001ff,0xfc003dff,0xf000ffff,
3481  0x7fff8,0x3fffc0,0x1fffe00,0xffff000,0x1f80003,0xffff803c,0x3c01e0,0x1e00f00,0xf007800,0x780f0001,0xe01ffffc,0x3c00078,0x0,
3482  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,
3483  0x3c1e003f,0xfffff078,0x30001e0f,0x300780,0x1e0,0x1e00,0x3c00,0x3dde00,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf800003e,
3484  0xf0780,0x3dfc000,0x783f8000,0xf8007,0xc01f00f0,0x3e0007,0xe000003f,0x1f,0xfc000000,0x7ff000,0xf80,0x3e007c70,0x783c003,0xc001e03c,
3485  0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,0x80007800,0x780,0x3c7c0000,0x7800001e,0x3878f078,0xf01e03c0,0x780780,0x1e0f000,0x1e078001,
3486  0xe03e0000,0xf000,0xf0003c0,0x1e007807,0x83f03c00,0x3ef00007,0xcf800000,0x3e00003c,0xf00,0x1e0,0xf80007,0xc0000000,0x0,0x3e01f801,
3487  0xfe07e001,0xf80f007e,0x7f801f8,0x1f801fff,0xfe00fc0f,0xf007f83f,0x1ffc00,0x7ff000,0x7807c00,0x1e0000f,0x87e1e01f,0xe0fc00fc,
3488  0xfc007f8,0x1f803f03,0xfc003df0,0x3807e03c,0x1fffff0,0x3c003c0,0x78003e0f,0x1e03,0xe03e00f8,0x3e00ff,0xffe0001e,0xf0,0x780,
3489  0x0,0x0,0x7800000,0xfe0000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0x1818000,0x7c00000,0x3,0xc00f0000,0xfe00000,0x3e00003,0xf800001f,
3490  0xc0000007,0xc0003e00,0x1e03c,0x3c0f80,0x0,0x0,0x0,0x70,0x380700fc,0x7800000,0x7c1fe,0x3e000fe0,0xffffe,0x1f3e00,0x0,0x780000,
3491  0x3f98e000,0xf000003c,0xfcf8007c,0xf800003c,0x3ffc,0x0,0x31c0001,0x80f00f80,0x380700,0x0,0x183,0x80e0c000,0x3f,0xe0000078,
3492  0x3c0,0x38,0x0,0x3c003c0,0xfffe1c00,0x0,0x0,0x38000078,0xf000e01,0xc003ffe0,0x1fff00,0x7ffc00,0xf000,0xf07800,0x783c000,0x3c1e0001,
3493  0xe0f0000f,0x7800078,0x3c000f07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,
3494  0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf801f01e,0xf3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78007cf8,
3495  0x1e000f,0x80f0f000,0x7c03f00,0x3e01f801,0xf00fc00f,0x807e007c,0x3f003e0,0x1f80707f,0x8f801f80,0xf003f03f,0x1f81f8,0xfc0fc0,
3496  0x7e07e00,0x3ff80001,0xffc0000f,0xfe00007f,0xf00003ff,0xfc003fc1,0xf801f81f,0x800fc0fc,0x7e07e0,0x3f03f00,0x1f81f800,0x1f80007,
3497  0xe07f003c,0x3c01e0,0x1e00f00,0xf007800,0x780f8003,0xe01fe07e,0x3e000f8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3498  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3f,0xfffff078,0x30000ffe,0x1f007c0,0x0,0x1e00,
3499  0x3c00,0xf9cf80,0x1e0000,0x0,0x0,0x0,0x78001e00,0x3c0001e,0x0,0xf00000fc,0x1e0780,0x3fff800,0x78ffe000,0xf0003,0xe03e00f0,
3500  0x3e0007,0xe000003f,0x7f,0xe01fffff,0xf00ffc00,0x1f80,0x3c01ff70,0x783c003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x1e0007,
3501  0x80007800,0x780,0x3cfc0000,0x7800001e,0x3c78f078,0xf01e03c0,0x780780,0x3e0f000,0x1e078003,0xc01f0000,0xf000,0xf0003c0,0x1e007807,
3502  0x83f83c00,0x1ff00003,0xcf000000,0x3e00003c,0xf00,0x1e0,0x0,0x0,0x0,0x20007801,0xfc03e003,0xe003007c,0x3f803e0,0x7c0003c,
3503  0xf807,0xf007e00f,0x3c00,0xf000,0x780f800,0x1e0000f,0x87e1f01f,0x803c00f8,0x7c007f0,0xf803e01,0xfc003f80,0x80f8004,0x3c000,
3504  0x3c003c0,0x3c003c0f,0x1e03,0xe03e0078,0x3c0000,0x7c0001e,0xf0,0x780,0x0,0x0,0x3ffff800,0x1ff0000,0x0,0x7800000,0x0,0x18,
3505  0xc0,0x0,0x1818000,0x3e00000,0x3,0xc00f0000,0x1ff00000,0x3e00007,0xfc00003f,0xe0000003,0xc0003c00,0xf03c,0x3c0f00,0x0,0x0,
3506  0x0,0x70,0x380701f0,0x800000,0x780fc,0x1e001ff0,0x7c,0xf3c00,0x0,0x780000,0x7e182000,0xf000001f,0xfff00ffc,0xffc0003c,0x3cfe,
3507  0x0,0x31c0001,0x80f01f80,0x780f00,0x0,0x183,0x80e0c000,0xf,0x80000078,0x780,0x38,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x38000078,
3508  0xf000f01,0xe003ffe0,0x1fff00,0x7ff800,0xf000,0xf07800,0x783c000,0x3c1e0001,0xe0f0000f,0x78000f8,0x3e000f07,0x8003c000,0x78000,
3509  0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f1e0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,
3510  0x78000f00,0x7c03e01e,0x1e3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78003cf0,0x1e0007,0x80f1e000,0x4000f00,0x20007801,0x3c008,
3511  0x1e0040,0xf00200,0x780403f,0x7803e00,0x3007c00f,0x803e007c,0x1f003e0,0xf801f00,0x780000,0x3c00000,0x1e000000,0xf00007f0,
3512  0x3e003f00,0x7801f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e003c,0x3c01e0,0x1e00f00,0xf007800,0x78078003,
3513  0xc01fc03e,0x1e000f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3514  0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xf078007c,0x300007fc,0x7e00fe0,0x0,0x1e00,0x3c00,0x3e1c3e0,0x1e0000,0x0,0x0,0x0,0xf0001e00,
3515  0x3c0001e,0x1,0xf000fff8,0x1e0780,0x3fffe00,0x79fff000,0x1f0001,0xfffc00f0,0x7e0007,0xe000003f,0x3ff,0x801fffff,0xf003ff80,
3516  0x3f00,0x3c03fff0,0xf01e003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3df80000,0x7800001e,
3517  0x1c70f078,0x781e03c0,0x780780,0x3c0f000,0x1e078007,0xc01f8000,0xf000,0xf0003c0,0x1e007807,0x83f83c00,0xfe00003,0xff000000,
3518  0x7c00003c,0x780,0x1e0,0x0,0x0,0x0,0x7c01,0xf801f007,0xc00100f8,0x1f803c0,0x3c0003c,0x1f003,0xf007c00f,0x80003c00,0xf000,
3519  0x783f000,0x1e0000f,0x3c0f01f,0x3e01f0,0x3e007e0,0x7c07c00,0xfc003f00,0xf0000,0x3c000,0x3c003c0,0x3c003c0f,0x1e01,0xf07c007c,
3520  0x7c0000,0xfc0001e,0xf0,0x780,0x0,0x0,0x3ffff000,0x3838000,0x0,0x7800000,0x0,0x18,0xc0,0x0,0xff0000,0x3f00000,0x3,0xc00fff00,
3521  0x38380000,0x7c0000e,0xe000070,0x70000001,0xe0003c00,0xf01e,0x780e00,0x0,0x0,0x0,0x0,0x1e0,0x0,0x780f8,0xf003838,0xfc,0xffc00,
3522  0x0,0x780000,0x7c180000,0xf000000f,0xffe00fff,0xffc0003c,0x783f,0x80000000,0x6380000,0xc0f83f80,0xf81f00,0x0,0x303,0x80e06000,
3523  0x0,0x78,0xf00,0x78,0x0,0x3c003c0,0x7ffe1c00,0x0,0x0,0x3800003c,0x3e000f81,0xf003ffe0,0x1fff00,0x1fc000,0xf000,0x1e03c00,
3524  0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e000f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,
3525  0x3c000001,0xe0001e00,0x3c0f0f0,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3e07c01e,0x1e3c0f0,0x3c0780,0x1e03c00,
3526  0xf01e000,0x78003ff0,0x1e0007,0x80f1e000,0xf80,0x7c00,0x3e000,0x1f0000,0xf80000,0x7c0001e,0x3c07c00,0x10078007,0x803c003c,
3527  0x1e001e0,0xf000f00,0x780000,0x3c00000,0x1e000000,0xf00007c0,0x1e003e00,0x7c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,
3528  0xf,0x801f003c,0x3c01e0,0x1e00f00,0xf007800,0x7807c007,0xc01f801f,0x1f001f0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3529  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x0,0xe078003c,0x300001f0,0x3f801ff0,0x0,
3530  0x3c00,0x1e00,0x3c1c1e0,0x1e0000,0x0,0x0,0x0,0xf0001e0f,0x3c0001e,0x3,0xe000fff0,0x3c0780,0x3ffff00,0x7bfff800,0x1e0000,0x7ff00078,
3531  0x7e0007,0xe000003f,0x1ffc,0x1fffff,0xf0007ff0,0x7e00,0x3c07c3f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,
3532  0x1fffff,0x80007800,0x780,0x3ffc0000,0x7800001e,0x1ef0f078,0x781e03c0,0x780780,0x7c0f000,0x1e07801f,0x800ff000,0xf000,0xf0003c0,
3533  0xf00f807,0x83b83c00,0xfc00001,0xfe000000,0xf800003c,0x780,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0xc00000f0,0xf80780,0x3c0003c,
3534  0x1e001,0xf007c007,0x80003c00,0xf000,0x787e000,0x1e0000f,0x3c0f01f,0x1e01e0,0x1e007c0,0x3c07800,0x7c003f00,0xf0000,0x3c000,
3535  0x3c003c0,0x3e007c07,0x80003c00,0xf8f8003c,0x780000,0xf80001e,0xf0,0x780,0x0,0x0,0x7ffff000,0x601c000,0x3,0xffff0000,0x0,
3536  0xfff,0xf8007fff,0xc0000000,0x7e003c,0x1fe0000,0xc0003,0xc00fff00,0x601c0000,0xf800018,0x70000c0,0x38000001,0xe0007800,0x701e,
3537  0x701e00,0x0,0x0,0x0,0x0,0x1e0,0x6,0x700f8,0xf00601c,0xf8,0x7f800,0x0,0x780000,0xf8180000,0xf000000f,0x87c00fff,0xffc0003c,
3538  0xf01f,0xc0000000,0x6380000,0xc07ff780,0x1f03e03,0xfffffe00,0x303,0x81c06000,0x0,0x1ffff,0xfe001e00,0x180f8,0x0,0x3c003c0,
3539  0x3ffe1c00,0x3f00000,0x0,0x3800003f,0xfe0007c0,0xf8000000,0x18000000,0xc0000006,0x1f000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,
3540  0x3c000f0,0x1e001f07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f0f0,
3541  0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f0f801e,0x3c3c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,
3542  0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07c00,0xf0007,0x8078003c,0x3c001e0,0x1e000f00,0x780000,0x3c00000,
3543  0x1e000000,0xf0000f80,0x1f003e00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0xf,0x3f003c,0x3c01e0,0x1e00f00,0xf007800,
3544  0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3545  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe078003f,0xb0000000,0xfc003cf0,0x0,0x3c00,0x1e00,0x101c040,0x1e0000,0x0,0x0,0x1,
3546  0xe0001e1f,0x83c0001e,0x7,0xe000fff0,0x3c0780,0x3c03f80,0x7fc0fc00,0x1e0000,0xfff80078,0xfe0007,0xe000003f,0x7fe0,0x1fffff,
3547  0xf0000ffc,0xfc00,0x780f81f0,0xf01e003,0xffff003c,0x1e0,0x3c03ff,0xffc01fff,0xfe03c000,0x1fffff,0x80007800,0x780,0x3ffc0000,
3548  0x7800001e,0x1ef0f078,0x3c1e03c0,0x780780,0x1fc0f000,0x1e07ffff,0x7ff00,0xf000,0xf0003c0,0xf00f007,0xc3b87c00,0x7c00001,0xfe000000,
3549  0xf800003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xf000f007,0x800000f0,0xf80780,0x1e0003c,0x1e001,0xf0078007,0x80003c00,0xf000,0x78fc000,
3550  0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,0x3c07800,0x7c003e00,0xf0000,0x3c000,0x3c003c0,0x1e007807,0x80003c00,0x7df0003c,0x780000,
3551  0x1f00001e,0xf0,0x780,0x0,0x0,0x7800000,0xe7ce000,0x3,0xffff0000,0x0,0xfff,0xf8007fff,0xc0000000,0x1f0,0xffe000,0x1c0003,
3552  0xc00fff00,0xe7ce0000,0xf800039,0xf38001cf,0x9c000000,0xe0007800,0x780e,0x701c00,0x0,0x0,0x0,0x0,0x1e0,0x7,0xf0078,0xf00e7ce,
3553  0x1f0,0x7f800,0x0,0x780000,0xf0180000,0xf000000e,0x1c0001f,0xe000003c,0xf007,0xe0000000,0x6380000,0xc03fe780,0x3e07c03,0xfffffe00,
3554  0x303,0xffc06000,0x0,0x1ffff,0xfe003ffe,0x1fff0,0x0,0x3c003c0,0x1ffe1c00,0x3f00000,0x7,0xffc0001f,0xfc0003e0,0x7c000001,0xfc00000f,
3555  0xe000007f,0x1e000,0x1e03c00,0xf01e000,0x780f0003,0xc078001e,0x3c000f0,0x1e001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,
3556  0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,
3557  0x783c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78001fe0,0x1e0007,0x80f1e000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c07800,
3558  0xf0003,0xc078001e,0x3c000f0,0x1e000780,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,
3559  0x7800780,0x3c003c00,0xf,0x7f003c,0x3c01e0,0x1e00f00,0xf007800,0x7803c007,0x801f000f,0xf001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3560  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe070001f,0xf8000007,
3561  0xf0007cf8,0x7800000,0x3c00,0x1e00,0x1c000,0x1e0000,0x0,0x0,0x1,0xe0001e1f,0x83c0001e,0xf,0xc000fff8,0x780780,0x2000f80,0x7f803e00,
3562  0x3e0003,0xfffe007c,0x1fe0000,0x0,0x3ff00,0x0,0x1ff,0x8001f000,0x780f00f0,0x1f00f003,0xffffc03c,0x1e0,0x3c03ff,0xffc01fff,
3563  0xfe03c00f,0xf81fffff,0x80007800,0x780,0x3ffe0000,0x7800001e,0xee0f078,0x3c1e03c0,0x7807ff,0xff80f000,0x1e07fffe,0x3ffe0,
3564  0xf000,0xf0003c0,0xf00f003,0xc7bc7800,0xfc00000,0xfc000001,0xf000003c,0x3c0,0x1e0,0x0,0x0,0x0,0x3c01,0xe000f80f,0x800001e0,
3565  0xf80f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x79f8000,0x1e0000f,0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003e00,
3566  0xf0000,0x3c000,0x3c003c0,0x1e007807,0x81e03c00,0x7df0003e,0xf80000,0x3e00003e,0xf0,0x7c0,0xfc000,0x80000000,0x7800000,0x1e7cf000,
3567  0x3,0xffff0000,0x0,0x18,0xc0,0x0,0xf80,0x7ffc00,0x380003,0xc00fff01,0xe7cf0000,0x1f000079,0xf3c003cf,0x9e000000,0xe0007000,
3568  0x380e,0xe01c00,0x0,0x0,0x0,0x0,0x1e0,0x3,0x800f0078,0xf01e7cf,0x3e0,0x3f000,0x0,0x780000,0xf018001f,0xfff8001e,0x1e0000f,
3569  0xc000003c,0xf003,0xe0000000,0x6380000,0xc00fc780,0x7c0f803,0xfffffe00,0x303,0xfe006000,0x0,0x1ffff,0xfe003ffe,0x1ffe0,0x0,
3570  0x3c003c0,0xffe1c00,0x3f00000,0x7,0xffc00007,0xf00001f0,0x3e00001f,0xfc0000ff,0xe00007ff,0x3e000,0x3e01e00,0x1f00f000,0xf8078007,
3571  0xc03c003e,0x1e001e0,0xf001e07,0xff83c000,0x7ffff,0x803ffffc,0x1ffffe0,0xfffff00,0xf00000,0x7800000,0x3c000001,0xe000fff8,
3572  0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000fc0,
3573  0x1e0007,0x80f1f000,0x780,0x3c00,0x1e000,0xf0000,0x780000,0x3c0001e,0x3c0f800,0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,
3574  0x3c00000,0x1e000000,0xf0000f00,0xf003c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1e,0xf7803c,0x3c01e0,0x1e00f00,
3575  0xf007800,0x7803e00f,0x801e000f,0x80f803e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3576  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1,0xe0f0000f,0xff00001f,0x8000f87c,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,
3577  0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x1f,0x800000fe,0xf00780,0x7c0,0x7f001e00,0x3c0007,0xe03f003f,0x3fe0000,0x0,0x3fc00,0x0,
3578  0x7f,0x8001e000,0x781f00f0,0x1e00f003,0xc007e03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3f9f0000,0x7800001e,
3579  0xfe0f078,0x3c1e03c0,0x7807ff,0xff00f000,0x1e07fff8,0xfff8,0xf000,0xf0003c0,0xf81f003,0xc7bc7800,0xfe00000,0x78000003,0xe000003c,
3580  0x1e0,0x1e0,0x0,0x0,0x0,0x1fffc01,0xe000780f,0x1e0,0x780f00,0x1e0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7bf0000,0x1e0000f,
3581  0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xf8000,0x3c000,0x3c003c0,0x1f00f807,0x81f03c00,0x3fe0001e,0xf00000,0x7c00007c,
3582  0xf0,0x3e0,0x3ff801,0x80000000,0x7800000,0x3cfcf800,0x3,0xffff0000,0x0,0x18,0xc0,0x0,0x7c00,0x1fff00,0x700003,0xc00f0003,
3583  0xcfcf8000,0x3e0000f3,0xf3e0079f,0x9f000000,0xf000,0x1000,0x0,0x0,0x0,0x0,0x0,0x1f0,0x1,0xc00f0078,0xf03cfcf,0x800007c0,0x1e000,
3584  0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x8000003c,0xf001,0xf0000000,0x6380000,0xc0000000,0xf81f003,0xfffffe00,0x303,
3585  0x87006000,0x0,0x1ffff,0xfe003ffe,0x7f00,0x0,0x3c003c0,0x3fe1c00,0x3f00000,0x7,0xffc00000,0xf8,0x1f0001ff,0xf0000fff,0x80007ffc,
3586  0xfc000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf001e07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,
3587  0x7800000,0x3c000001,0xe000fff8,0x3c0f078,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x3fc001e,0x1e03c0f0,0x3c0780,
3588  0x1e03c00,0xf01e000,0x78000780,0x1e0007,0x80f0fc00,0x3fff80,0x1fffc00,0xfffe000,0x7fff0003,0xfff8001f,0xffc0001e,0x3c0f000,
3589  0x1e0003,0xc0f0001e,0x78000f0,0x3c000780,0x780000,0x3c00000,0x1e000000,0xf0001e00,0xf803c00,0x3c078001,0xe03c000f,0x1e00078,
3590  0xf0003c0,0x78001e07,0xfffffe1e,0x1e7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801e00f,0x1e0007,0x807803c0,0x0,0x0,0x0,0x0,0x0,
3591  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00007,
3592  0xffc0007e,0xf03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x0,0x3,0xc0001e1f,0x83c0001e,0x3f,0x3e,0xf00780,0x3c0,0x7e001e00,
3593  0x7c000f,0x800f001f,0xffde0000,0x0,0x3e000,0x0,0xf,0x8003e000,0x781e0070,0x1e00f003,0xc001f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,
3594  0xf81e0007,0x80007800,0x780,0x3f1f0000,0x7800001e,0x7c0f078,0x1e1e03c0,0x7807ff,0xfc00f000,0x1e07fffe,0xffc,0xf000,0xf0003c0,
3595  0x781e003,0xc71c7800,0x1ff00000,0x78000003,0xe000003c,0x1e0,0x1e0,0x0,0x0,0x0,0xffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,
3596  0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7f000,0x3c000,
3597  0x3c003c0,0xf00f007,0xc1f07c00,0x1fc0001f,0x1f00000,0xfc000ff8,0xf0,0x1ff,0xfffe07,0x80000000,0x7800000,0x7ffcfc00,0x0,0xf000000,
3598  0x0,0x18,0xc0,0x0,0x3e000,0x1ff80,0xe00003,0xc00f0007,0xffcfc000,0x3e0001ff,0xf3f00fff,0x9f800000,0x6000,0x0,0x0,0x7c000,
3599  0x0,0x0,0x0,0xfe,0x0,0xe00f007f,0xff07ffcf,0xc0000fc0,0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00007,0x80000000,0xf800,
3600  0xf0000000,0x6380000,0xc0000000,0x1f03c000,0x1e00,0x303,0x83806000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xfe1c00,0x3f00000,0x0,
3601  0x0,0x3c,0xf801fff,0xfff8,0x7ffc0,0x1f8000,0x3c01e00,0x1e00f000,0xf0078007,0x803c003c,0x1e001e0,0xf003c07,0x8003c000,0x78000,
3602  0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,
3603  0x78000f00,0x1f8001e,0x1e03c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e000f,0x80f0ff00,0x1ffff80,0xffffc00,0x7fffe003,
3604  0xffff001f,0xfff800ff,0xffc007ff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,
3605  0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x3c7803c,0x3c01e0,0x1e00f00,0xf007800,0x7801f01f,
3606  0x1e0007,0x807c07c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3607  0x0,0x0,0x0,0x0,0x780000,0x3,0xc0f00000,0xfff003f0,0x1f00f03e,0x7800000,0x3c00,0x1e00,0x1c000,0x7fffff80,0x0,0x7ff80000,0x3,
3608  0xc0001e0f,0x3c0001e,0x7e,0x1f,0x1e00780,0x3e0,0x7e000f00,0x78000f,0x7800f,0xff9e0000,0x0,0x3fc00,0x0,0x7f,0x8003c000,0x781e0070,
3609  0x3e00f803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c00f,0xf81e0007,0x80007800,0x780,0x3e0f8000,0x7800001e,0x7c0f078,0x1e1e03c0,
3610  0x7807ff,0xf000f000,0x1e07807f,0xfe,0xf000,0xf0003c0,0x781e003,0xc71c7800,0x3ef00000,0x78000007,0xc000003c,0x1e0,0x1e0,0x0,
3611  0x0,0x0,0x1ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7ff0000,0x1e0000f,0x3c0f01e,
3612  0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x7ff80,0x3c000,0x3c003c0,0xf00f003,0xc1f07800,0x1fc0000f,0x1e00000,0xf8000ff0,0xf0,
3613  0xff,0xffffff,0x80000000,0x3fffc000,0xfff9fe00,0x0,0xf000000,0x0,0x18,0xc0,0x0,0x1f0000,0x1fc0,0x1c00003,0xc00f000f,0xff9fe000,
3614  0x7c0003ff,0xe7f81fff,0x3fc00000,0x0,0x0,0x0,0xfe000,0x1ffffc0f,0xfffffc00,0x0,0xff,0xf0000000,0x700f007f,0xff0fff9f,0xe0000f80,
3615  0x1e000,0x0,0x780001,0xe018001f,0xfff8001c,0xe00fff,0xffc00000,0xf800,0xf0000000,0x6380000,0xc0ffff80,0x3e078000,0x1e00,0x7ff80303,
3616  0x83c06000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,0x0,0x7f,0xff00001e,0x7c1fff0,0xfff80,0x7ffc00,0x3f0000,0x7c01f00,
3617  0x3e00f801,0xf007c00f,0x803e007c,0x1f003e0,0xf803c07,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3618  0xe0001e00,0x3c0f03c,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x1f8001e,0x3c03c0f0,0x3c0780,0x1e03c00,0xf01e000,
3619  0x78000780,0x1e001f,0xf07f80,0x3ffff80,0x1ffffc00,0xffffe007,0xffff003f,0xfff801ff,0xffc03fff,0xffc0f000,0x1fffff,0xc0fffffe,
3620  0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,
3621  0xfffffe1e,0x787803c,0x3c01e0,0x1e00f00,0xf007800,0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3622  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x3ff80fc0,0x7fc1e01f,
3623  0x7800000,0x3c00,0x1e00,0x0,0x7fffff80,0x0,0x7ff80000,0x7,0x80001e00,0x3c0001e,0xfc,0xf,0x1e00780,0x1e0,0x7c000f00,0x78000f,
3624  0x78007,0xff1e0000,0x0,0x3ff00,0x0,0x1ff,0x8003c000,0x781e0070,0x3c007803,0xc000f03c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,
3625  0x80007800,0x780,0x3c07c000,0x7800001e,0x7c0f078,0xf1e03c0,0x780780,0xf000,0x1e07801f,0x3e,0xf000,0xf0003c0,0x781e003,0xcf1c7800,
3626  0x3cf80000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,0x0,0x0,0x3ffffc01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,
3627  0x80003c00,0xf000,0x7ff8000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3fff0,0x3c000,0x3c003c0,0xf81f003,
3628  0xc3b87800,0xf80000f,0x1e00001,0xf0000ff0,0xf0,0xff,0xf03fff,0x80000000,0x3fff8001,0xfff1ff00,0x0,0xf000000,0x0,0x18,0xc0,
3629  0x0,0x380000,0x7c0,0x3c00003,0xc00f001f,0xff1ff000,0xf80007ff,0xc7fc3ffe,0x3fe00000,0x0,0x0,0x0,0x1ff000,0x7ffffe1f,0xffffff00,
3630  0x0,0x7f,0xfe000000,0x780f007f,0xff1fff1f,0xf0001f00,0x1e000,0x0,0x780001,0xe0180000,0xf000001c,0xe00fff,0xffc00000,0x7c00,
3631  0xf0000000,0x31c0001,0x80ffff80,0x3e078000,0x1e00,0x7ff80183,0x81c0c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x3f00000,
3632  0x0,0x7f,0xff00001e,0x7c7ff03,0xc03ff8fe,0x1ffc0f0,0x7e0000,0x7800f00,0x3c007801,0xe003c00f,0x1e0078,0xf003c0,0x7803c07,0x8003c000,
3633  0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,
3634  0xf0001e0,0x78000f00,0x3fc001e,0x7803c0f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e007f,0xf03fe0,0x7ffff80,0x3ffffc01,
3635  0xffffe00f,0xffff007f,0xfff803ff,0xffc07fff,0xffc0f000,0x1fffff,0xc0fffffe,0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,
3636  0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e07,0xfffffe1e,0x707803c,0x3c01e0,0x1e00f00,0xf007800,
3637  0x7800f01e,0x1e0007,0x803c0780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3638  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x30f81f00,0xffe1e00f,0x87800000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,
3639  0x7,0x80001e00,0x3c0001e,0x1f8,0x7,0x83c00780,0x1e0,0x7c000f00,0xf8001e,0x3c001,0xfc1e0000,0x0,0x7fe0,0x0,0xffc,0x3c000,0x781e0070,
3640  0x3ffff803,0xc000783c,0x1e0,0x3c03c0,0x1e00,0x3c000,0x781e0007,0x80007800,0x780,0x3c07c000,0x7800001e,0x380f078,0xf1e03c0,
3641  0x780780,0xf000,0x1e07800f,0x8000001e,0xf000,0xf0003c0,0x3c3c003,0xcf1e7800,0x7c780000,0x7800000f,0x8000003c,0xf0,0x1e0,0x0,
3642  0x0,0x0,0x7f003c01,0xe000780f,0x1e0,0x780fff,0xffe0003c,0x3c000,0xf0078007,0x80003c00,0xf000,0x7f7c000,0x1e0000f,0x3c0f01e,
3643  0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfff8,0x3c000,0x3c003c0,0x781e003,0xc3b87800,0x1fc00007,0x83e00003,0xe0000ff8,0xf0,
3644  0x1ff,0xc007fe,0x0,0x7fff8001,0xffe3ff00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x3c0,0x7800003,0xc00f001f,0xfe3ff000,0xf80007ff,
3645  0x8ffc3ffc,0x7fe00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x1f,0xff000000,0x3c0f007f,0xff1ffe3f,0xf0003e00,0x1e000,0x0,0x780001,
3646  0xe0180000,0xf000001e,0x1e00fff,0xffc00000,0x3f00,0xf0000000,0x31c0001,0x80ffff80,0x1f03c000,0x1e00,0x7ff80183,0x81c0c000,
3647  0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x7f,0xff00003c,0xf87f007,0xc03f83ff,0x81fc01f0,0x7c0000,0x7ffff00,0x3ffff801,
3648  0xffffc00f,0xfffe007f,0xfff003ff,0xff807fff,0x8003c000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3649  0xe0001e00,0x3c0f01e,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0x7fe001e,0xf003c0f0,0x3c0780,0x1e03c00,0xf01e000,
3650  0x78000780,0x1ffffe,0xf00ff0,0xfe00780,0x7f003c03,0xf801e01f,0xc00f00fe,0x7807f0,0x3c0ffff,0xffc0f000,0x1fffff,0xc0fffffe,
3651  0x7fffff0,0x3fffff80,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,
3652  0x1e,0xf07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783e,0x1e0007,0x801e0f80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3653  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1ff,0xffff8000,0x307c0801,0xe1f1e00f,0x87000000,
3654  0x3c00,0x1e00,0x0,0x1e0000,0x0,0x7ff80000,0xf,0x1e00,0x3c0001e,0x3f0,0x7,0x83fffffc,0x1e0,0x7c000f00,0xf0001e,0x3c000,0x3e0000,
3655  0x0,0x1ffc,0x1fffff,0xf0007ff0,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x3c000,0x781e0007,0x80007800,
3656  0x780,0x3c03e000,0x7800001e,0xf078,0x79e03c0,0x780780,0xf000,0x1e078007,0x8000000f,0xf000,0xf0003c0,0x3c3c001,0xee0ef000,
3657  0xf87c0000,0x7800001f,0x3c,0x78,0x1e0,0x0,0x0,0x0,0x7c003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00,
3658  0xf000,0x7e3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x1ffc,0x3c000,0x3c003c0,0x781e003,0xe3b8f800,
3659  0x1fc00007,0x83c00007,0xc00000fc,0xf0,0x3e0,0x8001f8,0x0,0x7800000,0xffc7fe00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,
3660  0xf000003,0xc00f000f,0xfc7fe001,0xf00003ff,0x1ff81ff8,0xffc00000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x3,0xff800000,0x1e0f0078,
3661  0xffc7f,0xe0007c00,0x1e000,0x0,0x780001,0xe0180000,0xf000000e,0x1c00007,0x80000000,0x1f81,0xe0000000,0x38e0003,0x80000000,
3662  0xf81f000,0x1e00,0x7ff801c3,0x80e1c000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf8,0x1f070007,0xc03803ff,0xc1c001f0,
3663  0xf80000,0xfffff00,0x7ffff803,0xffffc01f,0xfffe00ff,0xfff007ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,
3664  0xf00000,0x7800000,0x3c000001,0xe0001e00,0x780f00f,0x3c078000,0xf03c0007,0x81e0003c,0xf0001e0,0x78000f00,0xf9f001e,0xf003c0f0,
3665  0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1ffffc,0xf003f8,0xf800780,0x7c003c03,0xe001e01f,0xf00f8,0x7807c0,0x3c0fc1e,0xf000,
3666  0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,
3667  0xf0003c0,0x78001e00,0x1e,0x1e07803c,0x3c01e0,0x1e00f00,0xf007800,0x7800783c,0x1e0007,0x801e0f00,0x0,0x0,0x0,0x0,0x0,0x0,
3668  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xffff8000,0x303c0001,
3669  0xc071e007,0xcf000000,0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0xf,0xf00,0x780001e,0x7e0,0x7,0x83fffffc,0x1e0,0x7c000f00,0x1f0001e,
3670  0x3c000,0x3c0000,0x0,0x3ff,0x801fffff,0xf003ff80,0x3c000,0x781e0070,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,
3671  0x80007800,0x780,0x3c01f000,0x7800001e,0xf078,0x79e03c0,0xf00780,0xf000,0x3e078007,0xc000000f,0xf000,0xf0003c0,0x3c3c001,
3672  0xee0ef000,0xf03e0000,0x7800003e,0x3c,0x78,0x1e0,0x0,0x0,0x0,0xf8003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,
3673  0x80003c00,0xf000,0x7c3e000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0xfc,0x3c000,0x3c003c0,0x3c3e001,0xe7b8f000,
3674  0x3fe00007,0xc7c0000f,0xc000003e,0xf0,0x7c0,0x0,0x0,0x7c00000,0x7fcffc00,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x0,0x1e0,0x1e000003,
3675  0xc00f0007,0xfcffc003,0xe00001ff,0x3ff00ff9,0xff800000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x1f800000,0xf0f0078,0x7fcff,
3676  0xc000fc00,0x1e000,0x0,0x780001,0xe0180000,0xf000000f,0x87c00007,0x80000000,0xfe3,0xe0000000,0x18780c3,0x0,0x7c0f800,0x1e00,
3677  0xc3,0x80e18000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x1f0,0x3e00000f,0xc0000303,0xe00003f0,0xf00000,0xfffff80,
3678  0x7ffffc03,0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,
3679  0x3c000001,0xe0001e00,0x780f00f,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1f0f801f,0xe00780f0,0x3c0780,0x1e03c00,
3680  0xf01e000,0x78000780,0x1ffff8,0xf000f8,0x1f000780,0xf8003c07,0xc001e03e,0xf01f0,0x780f80,0x3c1f01e,0xf000,0x1e0000,0xf00000,
3681  0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,
3682  0x1e,0x3c07803c,0x3c01e0,0x1e00f00,0xf007800,0x78007c7c,0x1e0007,0x801f1f00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3683  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x81c00000,0x303c0003,0x8039e003,0xef000000,
3684  0x3c00,0x1e00,0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0xfc0,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,
3685  0x0,0x7f,0xe01fffff,0xf00ffc00,0x3c000,0x781f00f0,0x7ffffc03,0xc000781e,0x1e0,0x7803c0,0x1e00,0x1e000,0x781e0007,0x80007800,
3686  0x780,0x3c01f000,0x7800001e,0xf078,0x7de01e0,0xf00780,0x7800,0x3c078003,0xc000000f,0xf000,0xf0003c0,0x3e7c001,0xee0ef001,
3687  0xf01e0000,0x7800003e,0x3c,0x3c,0x1e0,0x0,0x0,0x0,0xf0003c01,0xe000780f,0x1e0,0x780f00,0x3c,0x3c000,0xf0078007,0x80003c00,
3688  0xf000,0x781f000,0x1e0000f,0x3c0f01e,0x1e03c0,0xf00780,0x1e0f000,0x3c003c00,0x3e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0x7df00003,
3689  0xc780000f,0x8000003e,0xf0,0x780,0x0,0x0,0x3c00000,0x3fcff800,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x1f00fc,0x1e0,0x1e000001,
3690  0xe00f0003,0xfcff8003,0xe00000ff,0x3fe007f9,0xff000000,0x0,0x0,0x0,0x1ff000,0x0,0x0,0x0,0x0,0x7c00000,0xf0f0078,0x3fcff,0x8000f800,
3691  0x1e000,0x0,0x780001,0xe0180000,0xf000001f,0xffe00007,0x8000003c,0x7ff,0xc0000000,0x1c3ffc7,0x0,0x3e07c00,0x1e00,0xe3,0x80738000,
3692  0x0,0x78,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0x3e0,0x7c00001d,0xc0000001,0xe0000770,0x1f00000,0xfffff80,0x7ffffc03,
3693  0xffffe01f,0xffff00ff,0xfff807ff,0xffc07fff,0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,
3694  0xe0001e00,0x780f00f,0x3c03c001,0xe01e000f,0xf00078,0x78003c0,0x3c001e00,0x3e07c01f,0xc00780f0,0x3c0780,0x1e03c00,0xf01e000,
3695  0x78000780,0x1fffc0,0xf0007c,0x1e000780,0xf0003c07,0x8001e03c,0xf01e0,0x780f00,0x3c1e01e,0xf000,0x1e0000,0xf00000,0x7800000,
3696  0x3c000000,0x780000,0x3c00000,0x1e000000,0xf0001e00,0x7803c00,0x3c078001,0xe03c000f,0x1e00078,0xf0003c0,0x78001e00,0x1e,0x7807803c,
3697  0x3c01e0,0x1e00f00,0xf007800,0x78003c78,0x1e0007,0x800f1e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3698  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x83c00000,0x303c0003,0x8039e001,0xee000000,0x1e00,0x3c00,
3699  0x0,0x1e0000,0x0,0x0,0x1e,0xf00,0x780001e,0x1f80,0x7,0x83fffffc,0x1e0,0x3c000f00,0x1e0001e,0x3c000,0x3c0000,0x0,0x1f,0xfc1fffff,
3700  0xf07ff000,0x0,0x780f00f0,0x78003c03,0xc000781e,0x1e0,0xf803c0,0x1e00,0x1e000,0x781e0007,0x80007800,0x780,0x3c00f800,0x7800001e,
3701  0xf078,0x3de01e0,0xf00780,0x7800,0x3c078003,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfe0ff003,0xe01f0000,0x7800007c,0x3c,0x3c,
3702  0x1e0,0x0,0x0,0x0,0xf0007c01,0xe000f80f,0x800001e0,0xf80f00,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x780f800,0x1e0000f,
3703  0x3c0f01e,0x1e03c0,0x1f00780,0x3e0f000,0x7c003c00,0x1e,0x3c000,0x3c003c0,0x3c3c001,0xe71cf000,0xf8f80003,0xe780001f,0x1e,
3704  0xf0,0x780,0x0,0x0,0x3c00000,0x1ffff000,0x0,0x1e000000,0x0,0x18,0xc0,0x0,0x3bc1de,0x1e0,0xf000001,0xe00f0001,0xffff0007,0xc000007f,
3705  0xffc003ff,0xfe000000,0x0,0x0,0x0,0xfe000,0x0,0x0,0x0,0x0,0x3c00000,0x1e0f0078,0x1ffff,0x1f000,0x1e000,0x0,0x780000,0xf0180000,
3706  0xf000001f,0xfff00007,0x8000003c,0x1ff,0x80000000,0xe0ff0e,0x0,0x1f03e00,0x1e00,0x70,0x70000,0x0,0x78,0x0,0x0,0x0,0x3c003c0,
3707  0xe1c00,0x0,0x0,0x0,0x7c0,0xf8000019,0xc0000000,0xe0000670,0x1e00000,0xf000780,0x78003c03,0xc001e01e,0xf00f0,0x780780,0x3c0f807,
3708  0x8001e000,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf80f007,0xbc03c001,0xe01e000f,
3709  0xf00078,0x78003c0,0x3c001e00,0x7c03e00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,
3710  0xf0007c07,0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0xf800,0x1e0000,0xf00000,0x7800000,0x3c000000,0x780000,0x3c00000,0x1e000000,
3711  0xf0001e00,0x7803c00,0x3c078003,0xe03c001f,0x1e000f8,0xf0007c0,0x78003e00,0x1f8001f,0xf00f803c,0x3c01e0,0x1e00f00,0xf007800,
3712  0x78003e78,0x1e000f,0x800f9e00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3713  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf,0x3c00000,0x303c0003,0x8039f001,0xfe000000,0x1e00,0x3c00,0x0,0x1e0000,0x0,0x0,0x3c,0xf00,
3714  0x780001e,0x3f00,0x7,0x80000780,0x3e0,0x3e000f00,0x3c0001e,0x3c000,0x7c0000,0x0,0x3,0xfe000000,0xff8000,0x0,0x3c0f81f0,0xf0001e03,
3715  0xc000780f,0x1e0,0xf003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x780,0x3c007c00,0x7800001e,0xf078,0x3de01e0,0xf00780,0x7800,
3716  0x3c078001,0xe000000f,0xf000,0xf0003c0,0x1e78001,0xfc07f003,0xe00f0000,0x78000078,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01,
3717  0xf000f007,0x800000f0,0xf80780,0x3c,0x1e001,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,
3718  0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78001,0xe71df000,0xf8f80001,0xef80003e,0x1e,0xf0,0x780,0x0,0x0,0x3c00000,
3719  0xfffe000,0x0,0x3e000000,0x0,0x18,0x7fff,0xc0000000,0x60c306,0x1e0,0x7800001,0xe00f0000,0xfffe0007,0x8000003f,0xff8001ff,
3720  0xfc000000,0x0,0x0,0x0,0x7c000,0x0,0x0,0x0,0x0,0x3c00000,0x3c0f0078,0xfffe,0x3e000,0x1e000,0x0,0x780000,0xf0180000,0xf000003c,
3721  0xfcf80007,0x8000003c,0x7f,0x0,0x70001c,0x0,0xf81f00,0x0,0x38,0xe0000,0x0,0x0,0x0,0x0,0x0,0x3c003c0,0xe1c00,0x0,0x0,0x0,0xf81,
3722  0xf0000039,0xc0000000,0xe0000e70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,0x8000f000,0x78000,
3723  0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0xf00f007,0xbc03c001,0xe01e000f,0xf00078,0x78003c0,
3724  0x3c001e00,0xf801f00f,0x800780f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,0x8003e03c,
3725  0x1f01e0,0xf80f00,0x7c1e01e,0x7800,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,0xf003c00,
3726  0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xe00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef8,0x1f000f,
3727  0x7be00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3728  0x0,0x0,0xf,0x3c00000,0x307c0003,0x8038f000,0xfc000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e00003c,0x780,0xf00001e,
3729  0x7e00,0xf,0x80000780,0x3c0,0x3e001e00,0x3c0001f,0x7c000,0x780007,0xe000003f,0x0,0xfe000000,0xfe0000,0x0,0x3c07c3f0,0xf0001e03,
3730  0xc000f80f,0x800001e0,0x1f003c0,0x1e00,0xf000,0x781e0007,0x80007800,0x4000f80,0x3c003c00,0x7800001e,0xf078,0x1fe01f0,0x1f00780,
3731  0x7c00,0x7c078001,0xf000001f,0xf000,0xf0003c0,0x1e78001,0xfc07f007,0xc00f8000,0x780000f8,0x3c,0x1e,0x1e0,0x0,0x0,0x0,0xf0007c01,
3732  0xf000f007,0xc00000f0,0xf80780,0x3c,0x1f003,0xf0078007,0x80003c00,0xf000,0x7807c00,0x1e0000f,0x3c0f01e,0x1e01e0,0x1e007c0,
3733  0x3c07800,0x7c003c00,0x1e,0x3c000,0x3c007c0,0x1e78000,0xfe0fe001,0xf07c0001,0xef00007c,0x1e,0xf0,0x780,0x0,0x0,0x1e00000,
3734  0x7cfc000,0xfc00000,0x3c00000f,0xc3f00000,0x18,0x7fff,0xc0000000,0x406303,0x3e0,0x3c00001,0xf00f0000,0x7cfc000f,0x8000001f,
3735  0x3f0000f9,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x780700f8,0x7cfc,0x7c000,0x1e000,0x0,0x780000,0xf8180000,
3736  0xf0000070,0x3c0007,0x8000003c,0x3f,0x80000000,0x3c0078,0x0,0x780f00,0x0,0x1e,0x3c0000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,0xe1c00,
3737  0x0,0x0,0x0,0xf01,0xe0000071,0xc0000000,0xe0001c70,0x1e00000,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,
3738  0x8000f800,0x78000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x1f00f003,0xfc03e003,0xe01f001f,
3739  0xf800f8,0x7c007c0,0x3e003e01,0xf000f80f,0xf00f0,0x3c0780,0x1e03c00,0xf01e000,0x78000780,0x1e0000,0xf0003c,0x1e000f80,0xf0007c07,
3740  0x8003e03c,0x1f01e0,0xf80f00,0x7c1e01e,0x7c00,0xf0000,0x780000,0x3c00000,0x1e000000,0x780000,0x3c00000,0x1e000000,0xf0000f00,
3741  0xf003c00,0x3c03c003,0xc01e001e,0xf000f0,0x7800780,0x3c003c00,0x1f8000f,0xc00f003c,0x7c01e0,0x3e00f00,0x1f007800,0xf8001ef0,
3742  0x1f000f,0x7bc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3743  0x0,0x0,0x0,0x0,0x780000,0xf,0x3800040,0x30780003,0x8038f800,0x78000000,0x1e00,0x3c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078,
3744  0x780,0x1f00001e,0xfc00,0x20001f,0x780,0x80007c0,0x1f001e00,0x7c0000f,0x78000,0xf80007,0xe000003f,0x0,0x1e000000,0xf00000,
3745  0x3c000,0x3c03fff0,0xf0001e03,0xc001f007,0x800101e0,0x7e003c0,0x1e00,0x7800,0x781e0007,0x80007800,0x6000f00,0x3c003e00,0x7800001e,
3746  0xf078,0x1fe00f0,0x1e00780,0x3c00,0x78078000,0xf020001e,0xf000,0x7800780,0xff0001,0xfc07f00f,0x8007c000,0x780001f0,0x3c,0xf,
3747  0x1e0,0x0,0x0,0x0,0xf800fc01,0xf801f007,0xc00100f8,0x1f807c0,0x40003c,0xf807,0xf0078007,0x80003c00,0xf000,0x7803e00,0x1f0000f,
3748  0x3c0f01e,0x1e01f0,0x3e007e0,0x7c07c00,0xfc003c00,0x1e,0x3e000,0x3e007c0,0x1ff8000,0xfe0fe003,0xe03e0001,0xff0000fc,0x1e,
3749  0xf0,0x780,0x0,0x0,0x1f00080,0x3cf8000,0xfc00000,0x3c00001f,0x83f00000,0x18,0xc0,0x0,0xc06203,0x40003c0,0x1c00000,0xf80f0000,
3750  0x3cf8001f,0xf,0x3e000079,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x700780fc,0x3cf8,0xfc000,0x1e000,0x0,0x780000,
3751  0x7c180000,0xf0000020,0x100007,0x8000003c,0xf,0x80000000,0x1f01f0,0x0,0x380700,0x0,0xf,0x80f80000,0x0,0x0,0x0,0x0,0x0,0x3e007c0,
3752  0xe1c00,0x0,0x0,0x0,0xe01,0xc0000071,0xc0000001,0xc0001c70,0x1e00040,0x1e0003c0,0xf0001e07,0x8000f03c,0x781e0,0x3c0f00,0x1e0f007,
3753  0x80007800,0x10078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e00,0x7e00f003,0xfc01e003,0xc00f001e,
3754  0x7800f0,0x3c00780,0x1e003c00,0xe000700f,0x800f0078,0x7803c0,0x3c01e00,0x1e00f000,0xf0000780,0x1e0000,0xf0003c,0x1f001f80,
3755  0xf800fc07,0xc007e03e,0x3f01f0,0x1f80f80,0xfc1e01f,0x7c00,0x100f8000,0x807c0004,0x3e00020,0x1f000100,0x780000,0x3c00000,0x1e000000,
3756  0xf0000f80,0x1f003c00,0x3c03e007,0xc01f003e,0xf801f0,0x7c00f80,0x3e007c00,0x1f8000f,0x801f003e,0x7c01f0,0x3e00f80,0x1f007c00,
3757  0xf8001ff0,0x1f801f,0x7fc00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3758  0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0xf,0x7800078,0x31f80001,0xc070fc00,0xfc000000,0x1e00,0x7c00,0x0,0x1e0000,0xfc0000,0x0,0x7e000078,
3759  0x7c0,0x1f00001e,0x1f000,0x38003f,0x780,0xe000f80,0x1f803e00,0x780000f,0x800f8000,0x1f00007,0xe000003f,0x0,0x2000000,0x800000,
3760  0x3c000,0x3e01ff71,0xf0001f03,0xc007f007,0xc00301e0,0x1fc003c0,0x1e00,0x7c00,0x781e0007,0x80007800,0x7801f00,0x3c001f00,0x7800001e,
3761  0xf078,0xfe00f8,0x3e00780,0x3e00,0xf8078000,0xf838003e,0xf000,0x7c00f80,0xff0000,0xfc07e00f,0x8003c000,0x780001e0,0x3c,0xf,
3762  0x1e0,0x0,0x0,0x0,0xf801fc01,0xfc03e003,0xe003007c,0x3f803e0,0x1c0003c,0xfc0f,0xf0078007,0x80003c00,0xf000,0x7801f00,0xf8000f,
3763  0x3c0f01e,0x1e00f8,0x7c007f0,0xf803e01,0xfc003c00,0x8003e,0x1f000,0x1e00fc0,0xff0000,0xfe0fe007,0xc01f0000,0xfe0000f8,0x1e,
3764  0xf0,0x780,0x0,0x0,0xf80180,0x1cf0000,0x1f800000,0x3c00001f,0x83e00000,0x18,0xc0,0x0,0xc06203,0x70007c0,0xe00000,0x7e0f0000,
3765  0x1cf0001e,0x7,0x3c000039,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x7c00000,0xe00780fc,0x2001cf0,0xf8000,0x1e000,0x0,
3766  0x780000,0x7e182000,0xf0000000,0x7,0x8000003c,0x7,0xc0000000,0x7ffc0,0x0,0x180300,0x0,0x3,0xffe00000,0x0,0x0,0x0,0x0,0x0,
3767  0x3f00fc0,0xe1c00,0x0,0x0,0x0,0xc01,0x800000e1,0xc0000003,0xc0003870,0x1f001c0,0x3e0003e1,0xf0001f0f,0x8000f87c,0x7c3e0,0x3e1f00,
3768  0x1f1e007,0x80007c00,0x30078000,0x3c0000,0x1e00000,0xf000000,0xf00000,0x7800000,0x3c000001,0xe0001e03,0xfc00f001,0xfc01f007,
3769  0xc00f803e,0x7c01f0,0x3e00f80,0x1f007c00,0x4000201f,0xc01f007c,0xf803e0,0x7c01f00,0x3e00f801,0xf0000780,0x1e0000,0xf0007c,
3770  0x1f003f80,0xf801fc07,0xc00fe03e,0x7f01f0,0x3f80f80,0x1fc1f03f,0x803e00,0x3007c003,0x803e001c,0x1f000e0,0xf800700,0x780000,
3771  0x3c00000,0x1e000000,0xf00007c0,0x3e003c00,0x3c01f00f,0x800f807c,0x7c03e0,0x3e01f00,0x1f00f800,0x1f80007,0xc03e001e,0xfc00f0,
3772  0x7e00780,0x3f003c01,0xf8000fe0,0x1fc03e,0x3f800,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3773  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,0xfff00001,0xe0f07f03,0xfe000000,0xf00,0x7800,0x0,
3774  0x1e0000,0xfc0000,0x0,0x7e0000f0,0x3f0,0x7e000fff,0xfc03ffff,0xf83f00fe,0x780,0xfc03f80,0xfc0fc00,0xf800007,0xe03f0018,0x7e00007,
3775  0xe000003f,0x0,0x0,0x0,0x3c000,0x1e007c71,0xe0000f03,0xffffe003,0xf01f01ff,0xff8003ff,0xffe01e00,0x3f01,0xf81e0007,0x803ffff0,
3776  0x7e03f00,0x3c000f00,0x7ffffe1e,0xf078,0xfe007e,0xfc00780,0x1f83,0xf0078000,0x783f00fe,0xf000,0x3f03f00,0xff0000,0xfc07e01f,
3777  0x3e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7e07fc01,0xfe07e001,0xf80f007e,0x7f801f8,0xfc0003c,0x7ffe,0xf0078007,
3778  0x807ffffe,0xf000,0x7801f00,0xfff00f,0x3c0f01e,0x1e00fc,0xfc007f8,0x1f803f03,0xfc003c00,0xf80fc,0x1fff0,0x1f83fc0,0xff0000,
3779  0xfc07e007,0xc01f0000,0xfe0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfe0780,0xfe0000,0x1f000000,0x3c00001f,0x7c00e03,0x81c00018,
3780  0xc0,0x0,0x406203,0x7e01fc0,0x700000,0x7fffff80,0xfe0003f,0xffffc003,0xf800001f,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f0,
3781  0x1f800001,0xc007c1fe,0x6000fe0,0x1ffffe,0x1e000,0x0,0x780000,0x3f98e03f,0xffff8000,0x7,0x8000003c,0x7,0xc0000000,0xfe00,
3782  0x0,0x80100,0x0,0x0,0x7f000000,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3f83fe8,0xe1c00,0x0,0x0,0x0,0x801,0xc1,0xc0000007,0x80003070,
3783  0xfc0fc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc03f01,0xf007ffff,0xc03ffffe,0x1fffff0,0xfffff80,0x7fffe003,
3784  0xffff001f,0xfff800ff,0xffc01fff,0xf800f001,0xfc00fc1f,0x8007e0fc,0x3f07e0,0x1f83f00,0xfc1f800,0x1f,0xf07e003f,0x3f001f8,
3785  0x1f800fc0,0xfc007e07,0xe0000780,0x1e0000,0xf301f8,0xfc0ff80,0x7e07fc03,0xf03fe01f,0x81ff00fc,0xff807e0,0x7fc0f87f,0x81801f80,
3786  0xf003f01f,0x801f80fc,0xfc07e0,0x7e03f00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff807e0,0x7e003c00,0x3c01f81f,0x800fc0fc,0x7e07e0,
3787  0x3f03f00,0x1f81f800,0x1f8000f,0xe07e001f,0x83fc00fc,0x1fe007e0,0xff003f07,0xf8000fe0,0x1fe07e,0x3f800,0x0,0x0,0x0,0x0,0x0,
3788  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x780007f,
3789  0xffe00000,0xffe03fff,0xdf000000,0xf00,0x7800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0x1ff,0xfc000fff,0xfc03ffff,0xf83ffffc,0x780,
3790  0xfffff00,0x7fff800,0xf000007,0xffff001f,0xffe00007,0xe000003f,0x0,0x0,0x0,0x3c000,0x1e000001,0xe0000f03,0xffffc001,0xffff01ff,
3791  0xff0003ff,0xffe01e00,0x1fff,0xf81e0007,0x803ffff0,0x7fffe00,0x3c000f80,0x7ffffe1e,0xf078,0xfe003f,0xff800780,0xfff,0xf0078000,
3792  0x7c3ffffc,0xf000,0x3ffff00,0xff0000,0xf803e01e,0x1e000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x7fffbc01,0xffffc000,
3793  0xffff003f,0xfff800ff,0xffc0003c,0x3ffe,0xf0078007,0x807ffffe,0xf000,0x7800f80,0x7ff00f,0x3c0f01e,0x1e007f,0xff8007ff,0xff001fff,
3794  0xbc003c00,0xffffc,0x1fff0,0x1fffbc0,0xff0000,0x7c07c00f,0x800f8000,0x7e0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x7fff80,0x7c0000,
3795  0x1f000000,0x3c00001e,0x7c00f07,0xc1e00018,0xc0,0x0,0x60e303,0x7ffff80,0x380000,0x3fffff80,0x7c0003f,0xffffc001,0xf000000f,
3796  0x80000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff800003,0x8003ffff,0xfe0007c0,0x1ffffe,0x1e000,0x0,0x780000,0x1fffe03f,0xffff8000,
3797  0x7,0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3fffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x1c1,
3798  0xc000000f,0x7070,0x7fffc0,0x3c0001e1,0xe0000f0f,0x7878,0x3c3c0,0x1e1e00,0xf1e007,0xffc01fff,0xf007ffff,0xc03ffffe,0x1fffff0,
3799  0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xf000f001,0xfc007fff,0x3fff8,0x1fffc0,0xfffe00,0x7fff000,0x3b,0xfffc003f,
3800  0xfff001ff,0xff800fff,0xfc007fff,0xe0000780,0x1e0000,0xf3fff8,0xffff780,0x7fffbc03,0xfffde01f,0xffef00ff,0xff7807ff,0xfbc0ffff,
3801  0xff800fff,0xf001ffff,0x800ffffc,0x7fffe0,0x3ffff00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff803ff,0xfc003c00,0x3c00ffff,0x7fff8,
3802  0x3fffc0,0x1fffe00,0xffff000,0x1f,0xfffc001f,0xffbc00ff,0xfde007ff,0xef003fff,0x780007e0,0x1ffffc,0x1f800,0x0,0x0,0x0,0x0,
3803  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0x700003f,
3804  0xffc00000,0x7fc01fff,0x9f800000,0xf80,0xf800,0x0,0x0,0xfc0000,0x0,0x7e0000f0,0xff,0xf8000fff,0xfc03ffff,0xf83ffff8,0x780,
3805  0xffffe00,0x7fff000,0xf000003,0xfffe001f,0xffc00007,0xe000003f,0x0,0x0,0x0,0x3c000,0xf000003,0xe0000f83,0xffff0000,0xffff01ff,
3806  0xfc0003ff,0xffe01e00,0xfff,0xf01e0007,0x803ffff0,0x7fffc00,0x3c0007c0,0x7ffffe1e,0xf078,0x7e003f,0xff000780,0x7ff,0xe0078000,
3807  0x3c3ffff8,0xf000,0x1fffe00,0x7e0000,0xf803e03e,0x1f000,0x780003ff,0xfffc003c,0x7,0x800001e0,0x0,0x0,0x0,0x3fff3c01,0xefff8000,
3808  0x7ffe001f,0xff78007f,0xff80003c,0x1ffc,0xf0078007,0x807ffffe,0xf000,0x78007c0,0x3ff00f,0x3c0f01e,0x1e003f,0xff0007bf,0xfe000fff,
3809  0xbc003c00,0xffff8,0xfff0,0xfff3c0,0x7e0000,0x7c07c01f,0x7c000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0x3fff80,0x380000,
3810  0x3e000000,0x7c00003e,0x7801f07,0xc1e00018,0xc0,0x0,0x39c1ce,0x7ffff00,0x1c0000,0xfffff80,0x380003f,0xffffc000,0xe0000007,
3811  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0xff000007,0x1ffcf,0xfe000380,0x1ffffe,0x1e000,0x0,0x780000,0xfffe03f,0xffff8000,0x7,
3812  0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dffdf8,0xe1c00,0x0,0x0,0x0,0x0,0x381,
3813  0xc000001e,0xe070,0x7fff80,0x7c0001f3,0xe0000f9f,0x7cf8,0x3e7c0,0x1f3e00,0xfbe007,0xffc00fff,0xf007ffff,0xc03ffffe,0x1fffff0,
3814  0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01fff,0xc000f000,0xfc007ffe,0x3fff0,0x1fff80,0xfffc00,0x7ffe000,0x79,0xfff8001f,
3815  0xffe000ff,0xff0007ff,0xf8003fff,0xc0000780,0x1e0000,0xf3fff0,0x7ffe780,0x3fff3c01,0xfff9e00f,0xffcf007f,0xfe7803ff,0xf3c07ff3,
3816  0xff8007ff,0xe000ffff,0x7fff8,0x3fffc0,0x1fffe00,0xfffffc07,0xffffe03f,0xffff01ff,0xfff801ff,0xf8003c00,0x3c007ffe,0x3fff0,
3817  0x1fff80,0xfffc00,0x7ffe000,0x1d,0xfff8000f,0xff3c007f,0xf9e003ff,0xcf001ffe,0x780007c0,0x1efff8,0x1f000,0x0,0x0,0x0,0x0,
3818  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780000,0x1e,0xf000003,
3819  0xfe000000,0x1f000fff,0xfc00000,0x780,0xf000,0x0,0x0,0xf80000,0x0,0x7e0001e0,0x7f,0xf0000fff,0xfc03ffff,0xf81ffff0,0x780,
3820  0x7fff800,0x1ffe000,0x1f000000,0xfff8001f,0xff000007,0xe000003e,0x0,0x0,0x0,0x3c000,0xf800003,0xc0000783,0xfff80000,0x3ffe01ff,
3821  0xe00003ff,0xffe01e00,0x7ff,0xc01e0007,0x803ffff0,0x3fff800,0x3c0003c0,0x7ffffe1e,0xf078,0x7e000f,0xfe000780,0x3ff,0xc0078000,
3822  0x3e1fffe0,0xf000,0x7ff800,0x7e0000,0xf803e07c,0xf800,0x780003ff,0xfffc003c,0x3,0xc00001e0,0x0,0x0,0x0,0xffe3c01,0xe7ff0000,
3823  0x3ffc000f,0xfe78003f,0xfe00003c,0x7f0,0xf0078007,0x807ffffe,0xf000,0x78003e0,0xff00f,0x3c0f01e,0x1e001f,0xfe00079f,0xfc0007ff,
3824  0x3c003c00,0x7ffe0,0x1ff0,0x7fe3c0,0x7e0000,0x7c07c03e,0x3e000,0x7c0001ff,0xffe0001e,0xf0,0x780,0x0,0x0,0xfff00,0x100000,
3825  0x3e000000,0x7800003c,0xf800f07,0xc1e00018,0xc0,0x0,0x1f80fc,0x3fffc00,0xc0000,0x3ffff80,0x100003f,0xffffc000,0x40000002,
3826  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xff,0xfc000006,0xff87,0xfc000100,0x1ffffe,0x1e000,0x0,0x780000,0x3ffc03f,0xffff8000,0x7,
3827  0x8000003c,0x3,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ffff,0xfe000000,0x0,0x0,0x3dff9f8,0xe1c00,0x0,0x0,0x0,0x0,0x3ff,
3828  0xf800003c,0xfffe,0x1ffe00,0x780000f3,0xc000079e,0x3cf0,0x1e780,0xf3c00,0x7bc007,0xffc003ff,0xe007ffff,0xc03ffffe,0x1fffff0,
3829  0xfffff80,0x7fffe003,0xffff001f,0xfff800ff,0xffc01ffc,0xf000,0xfc001ffc,0xffe0,0x7ff00,0x3ff800,0x1ffc000,0x70,0xfff00007,
3830  0xff80003f,0xfc0001ff,0xe0000fff,0x780,0x1e0000,0xf3ffe0,0x1ffc780,0xffe3c00,0x7ff1e003,0xff8f001f,0xfc7800ff,0xe3c03fe1,
3831  0xff0003ff,0xc0007ffc,0x3ffe0,0x1fff00,0xfff800,0xfffffc07,0xffffe03f,0xffff01ff,0xfff800ff,0xf0003c00,0x3c003ffc,0x1ffe0,
3832  0xfff00,0x7ff800,0x3ffc000,0x38,0xfff00007,0xfe3c003f,0xf1e001ff,0x8f000ffc,0x780007c0,0x1e7ff0,0x1f000,0x0,0x0,0x0,0x0,0x0,
3833  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,
3834  0x1fc,0x0,0x780,0xf000,0x0,0x0,0x1f80000,0x0,0x1e0,0x1f,0xc0000000,0x0,0x1ff80,0x0,0xffc000,0x7f8000,0x0,0x3fe00007,0xfc000000,
3835  0x7e,0x0,0x0,0x0,0x0,0x7c00000,0x0,0x0,0xff00000,0x0,0x0,0xfe,0x0,0x0,0x3fc000,0x0,0x0,0x0,0x3,0xf8000000,0xff,0xc0000000,
3836  0x1ff00,0x0,0x1fe000,0x0,0x0,0x0,0x0,0x3c,0x3,0xc00001e0,0x0,0x0,0x0,0x3f80000,0x1fc0000,0x7f00003,0xf8000007,0xf0000000,
3837  0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x7,0xf8000787,0xf00001fc,0x3c000000,0x7f80,0x0,0x1f8000,0x0,0x0,0x0,0x7c000000,0x1e,
3838  0xf0,0x780,0x0,0x0,0x3fc00,0x0,0x3c000000,0x7800003c,0xf000601,0xc00018,0xc0,0x0,0x0,0x3fe000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3839  0x0,0x0,0x0,0x0,0x0,0x0,0xf,0xf0000000,0x7e03,0xf0000000,0x0,0x0,0x0,0x0,0xfe0000,0x0,0x0,0x3c,0x2007,0x80000000,0x0,0x0,
3840  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c7e0f0,0xe1c00,0x0,0x3800000,0x0,0x0,0x3ff,0xf8000078,0xfffe,0x7f800,0x0,0x0,0x0,0x0,
3841  0x0,0x0,0xff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f0,0x3f80,0x1fc00,0xfe000,0x7f0000,0x70,0x3fc00001,0xfe00000f,0xf000007f,
3842  0x800003fc,0x0,0x0,0xff00,0x7f0000,0x3f80000,0x1fc00000,0xfe000007,0xf000003f,0x80001f80,0xfc00007f,0xfe0,0x7f00,0x3f800,
3843  0x1fc000,0x0,0x0,0x0,0x3f,0xc0000000,0xff0,0x7f80,0x3fc00,0x1fe000,0xff0000,0x78,0x3fc00001,0xf800000f,0xc000007e,0x3f0,0x7c0,
3844  0x1e1fc0,0x1f000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3845  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3846  0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0xe0000000,0x0,0x0,0x0,
3847  0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,
3848  0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,0x1e,0xf0,0x780,0x0,0x0,0x0,0x0,0x3c000000,0x78000078,0xf000000,0x18,0xc0,0x0,0x0,0x0,
3849  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3c0f,0x80000000,
3850  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0x1800000,0x0,0x0,0x3ff,0xf80000f0,0xfffe,0x0,0x0,0x0,0x0,
3851  0x0,0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3852  0x0,0x0,0xc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x0,0x0,0x0,0x0,0x780,0x1e0000,0x1e000,0x0,0x0,0x0,
3853  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,
3854  0x0,0x0,0x3c0,0x1e000,0x0,0x0,0x1f00000,0x0,0x3c0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0x1f80000,
3855  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0xf0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x1,0xe00001e0,0x0,
3856  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf8000000,
3857  0x1f,0xf0,0xf80,0x0,0x0,0x0,0x0,0x78000000,0xf8000078,0x1e000000,0x8,0x40,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3858  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3fff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3859  0x0,0x3c00000,0xe1c00,0x0,0x1c00000,0x0,0x0,0x1,0xc00001e0,0x70,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3860  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3861  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf80,0x1e0000,0x3e000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3862  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x3c000,0x0,0x0,0x1f00000,
3863  0x0,0x780,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7c,0x0,0x0,0x0,0x0,0xfe0100,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3864  0x0,0x0,0x0,0x0,0xf8000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0xf0007fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0xe0000000,
3865  0x0,0xf000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x0,0xf0000000,0x1f,0x800000f0,0x1f80,0x0,0x0,0x0,0x0,
3866  0x78000000,0xf0000070,0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3867  0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x3ffe,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,
3868  0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3869  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3870  0x0,0x0,0x0,0xf00,0x1e0000,0x3c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3871  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0x1e0,0x7c000,0x0,0x0,0x1e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3872  0x0,0x0,0x0,0x0,0x0,0x78,0x0,0x0,0x0,0x0,0x7fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x78000000,
3873  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4003,0xe0000000,0x0,0x1f000,0x0,0x0,
3874  0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x1,0xf0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0x70000001,0xf00000e0,
3875  0x1c000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,
3876  0x0,0x0,0x3c,0xff8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0xe1c00,0x0,0xe00000,0x0,0x0,0x1,0xc00003ff,
3877  0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3878  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1f00,0x1e0000,
3879  0x7c000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3880  0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,0xf0,0x78000,0x0,0x0,0x3e00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf8,0x0,
3881  0x0,0x0,0x0,0x1fff80,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x20000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,
3882  0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x780f,0xc0000000,0x0,0x3e000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,
3883  0x0,0x0,0x0,0x0,0x3,0xe0000000,0xf,0xfc0000f0,0x3ff00,0x0,0x0,0x0,0x0,0xf0000103,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3884  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,
3885  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x21e00000,0x0,0x0,0x1,0xc00003ff,0xe0000070,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,
3886  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x10f,0x0,
3887  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3e00,0x1e0000,0xf8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3888  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x30000000,0x0,0x0,
3889  0xf8,0xf8000,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x1fe00,0x0,0x0,0x0,0x0,
3890  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3f,0xf0000000,0x7fe0,0x0,0x0,0x0,0x0,0x0,0x0,
3891  0x0,0x0,0x7fff,0xc0000000,0x0,0x3ffe000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0xe0000000,0x7,0xfc0000f0,
3892  0x3fe00,0x0,0x0,0x0,0x0,0x600001ff,0xe0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3893  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x180000,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,
3894  0x3fe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3895  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3896  0x0,0x0,0x0,0x0,0x7fe00,0x1e0000,0x1ff8000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3897  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3898  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3899  0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fff,0x80000000,0x0,0x3ffc000,0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,
3900  0x0,0x0,0x0,0x0,0x7f,0xc0000000,0x0,0xfc0000f0,0x3f000,0x0,0x0,0x0,0x0,0x1ff,0xc0000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3901  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3902  0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3fc00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,
3903  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fe,0x0,0x0,0x0,0x0,0x0,0x0,
3904  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7fc00,0x1e0000,0x1ff0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3905  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3906  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3907  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x3ffe,0x0,0x0,0x3ff8000,0x0,0x0,0x0,
3908  0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7f,0x80000000,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x1ff,0x80000000,0x0,0x0,0x0,0x0,
3909  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3910  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x3f800000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,
3911  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fc,0x0,0x0,
3912  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f800,0x1e0000,0x1fe0000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3913  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3914  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3915  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x7f8,0x0,0x0,0x3fe0000,
3916  0x0,0x0,0x0,0x0,0x780,0x0,0x3c000000,0x0,0x0,0x0,0x0,0x0,0x7e,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0xfe,0x0,0x0,0x0,0x0,0x0,0x0,
3917  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3918  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3c00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3919  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3920  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x7e000,0x1e0000,0x1f80000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3921  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3922  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3923  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1fffffe0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3924  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3925  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3926  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3927  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3928  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3929  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3930  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3931  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xf0,0x0,0x0,0x0,
3932  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3933  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3934  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
3935  0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0 };
3936 
3937  // Define a 40x38 'danger' color logo (used by cimg::dialog()).
3938  const unsigned char logo40x38[4576] = {
3939  177,200,200,200,3,123,123,0,36,200,200,200,1,123,123,0,2,255,255,0,1,189,189,189,1,0,0,0,34,200,200,200,
3940  1,123,123,0,4,255,255,0,1,189,189,189,1,0,0,0,1,123,123,123,32,200,200,200,1,123,123,0,5,255,255,0,1,0,0,
3941  0,2,123,123,123,30,200,200,200,1,123,123,0,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,29,200,200,200,
3942  1,123,123,0,7,255,255,0,1,0,0,0,2,123,123,123,28,200,200,200,1,123,123,0,8,255,255,0,1,189,189,189,1,0,0,0,
3943  2,123,123,123,27,200,200,200,1,123,123,0,9,255,255,0,1,0,0,0,2,123,123,123,26,200,200,200,1,123,123,0,10,255,
3944  255,0,1,189,189,189,1,0,0,0,2,123,123,123,25,200,200,200,1,123,123,0,3,255,255,0,1,189,189,189,3,0,0,0,1,189,
3945  189,189,3,255,255,0,1,0,0,0,2,123,123,123,24,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,3,255,255,0,1,189,
3946  189,189,1,0,0,0,2,123,123,123,23,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,4,255,255,0,1,0,0,0,2,123,123,123,
3947  22,200,200,200,1,123,123,0,5,255,255,0,5,0,0,0,4,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,21,200,200,200,
3948  1,123,123,0,5,255,255,0,5,0,0,0,5,255,255,0,1,0,0,0,2,123,123,123,20,200,200,200,1,123,123,0,6,255,255,0,5,0,0,
3949  0,5,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,19,200,200,200,1,123,123,0,6,255,255,0,1,123,123,0,3,0,0,0,1,
3950  123,123,0,6,255,255,0,1,0,0,0,2,123,123,123,18,200,200,200,1,123,123,0,7,255,255,0,1,189,189,189,3,0,0,0,1,189,
3951  189,189,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,17,200,200,200,1,123,123,0,8,255,255,0,3,0,0,0,8,255,255,
3952  0,1,0,0,0,2,123,123,123,16,200,200,200,1,123,123,0,9,255,255,0,1,123,123,0,1,0,0,0,1,123,123,0,8,255,255,0,1,189,
3953  189,189,1,0,0,0,2,123,123,123,15,200,200,200,1,123,123,0,9,255,255,0,1,189,189,189,1,0,0,0,1,189,189,189,9,255,255,
3954  0,1,0,0,0,2,123,123,123,14,200,200,200,1,123,123,0,11,255,255,0,1,0,0,0,10,255,255,0,1,189,189,189,1,0,0,0,2,123,
3955  123,123,13,200,200,200,1,123,123,0,23,255,255,0,1,0,0,0,2,123,123,123,12,200,200,200,1,123,123,0,11,255,255,0,1,189,
3956  189,189,2,0,0,0,1,189,189,189,9,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,11,200,200,200,1,123,123,0,11,255,255,
3957  0,4,0,0,0,10,255,255,0,1,0,0,0,2,123,123,123,10,200,200,200,1,123,123,0,12,255,255,0,4,0,0,0,10,255,255,0,1,189,189,
3958  189,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,12,255,255,0,1,189,189,189,2,0,0,0,1,189,189,189,11,255,255,0,1,
3959  0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,27,255,255,0,1,0,0,0,3,123,123,123,8,200,200,200,1,123,123,0,26,255,
3960  255,0,1,189,189,189,1,0,0,0,3,123,123,123,9,200,200,200,1,123,123,0,24,255,255,0,1,189,189,189,1,0,0,0,4,123,123,
3961  123,10,200,200,200,1,123,123,0,24,0,0,0,5,123,123,123,12,200,200,200,27,123,123,123,14,200,200,200,25,123,123,123,86,
3962  200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0};
3963 
3965 
3969  inline std::FILE* output(std::FILE *file) {
3970  static std::FILE *res = stderr;
3971  if (file) res = file;
3972  return res;
3973  }
3974 
3976 
3989  inline void warn(const char *const format, ...) {
3990  if (cimg::exception_mode()>=1) {
3991  char message[16384] = { 0 };
3992  std::va_list ap;
3993  va_start(ap,format);
3994  cimg_vsnprintf(message,sizeof(message),format,ap);
3995  va_end(ap);
3996 #ifdef cimg_strict_warnings
3997  throw CImgWarningException(message);
3998 #else
3999  std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s",cimg::t_red,cimg::t_normal,message);
4000 #endif
4001  }
4002  }
4003 
4004  // Execute an external system command.
4013  inline int system(const char *const command, const char *const module_name=0) {
4014 #if cimg_OS==2
4015  PROCESS_INFORMATION pi;
4016  STARTUPINFO si;
4017  std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
4018  std::memset(&si,0,sizeof(STARTUPINFO));
4019  GetStartupInfo(&si);
4020  si.cb = sizeof(si);
4021  si.wShowWindow = SW_HIDE;
4022  si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
4023  const BOOL res = CreateProcess((LPCTSTR)module_name,(LPTSTR)command,0,0,FALSE,0,0,0,&si,&pi);
4024  if (res) {
4025  WaitForSingleObject(pi.hProcess, INFINITE);
4026  CloseHandle(pi.hThread);
4027  CloseHandle(pi.hProcess);
4028  return 0;
4029  } else
4030 #endif
4031  return std::system(command);
4032  return module_name?0:1;
4033  }
4034 
4036  template<typename T>
4037  inline T& temporary(const T&) {
4038  static T temp;
4039  return temp;
4040  }
4041 
4043  template<typename T>
4044  inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
4045 
4047  template<typename T1, typename T2>
4048  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
4049  cimg::swap(a1,b1); cimg::swap(a2,b2);
4050  }
4051 
4053  template<typename T1, typename T2, typename T3>
4054  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
4055  cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
4056  }
4057 
4059  template<typename T1, typename T2, typename T3, typename T4>
4060  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
4061  cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
4062  }
4063 
4065  template<typename T1, typename T2, typename T3, typename T4, typename T5>
4066  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
4067  cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
4068  }
4069 
4071  template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
4072  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) {
4073  cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
4074  }
4075 
4077  template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
4078  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6,
4079  T7& a7, T7& b7) {
4080  cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
4081  }
4082 
4084  template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
4085  inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6,
4086  T7& a7, T7& b7, T8& a8, T8& b8) {
4087  cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
4088  }
4089 
4091 
4094  inline bool endianness() {
4095  const int x = 1;
4096  return ((unsigned char*)&x)[0]?false:true;
4097  }
4098 
4100 
4104  template<typename T>
4105  inline void invert_endianness(T* const buffer, const unsigned long size) {
4106  if (size) switch (sizeof(T)) {
4107  case 1 : break;
4108  case 2 : { for (unsigned short *ptr = (unsigned short*)buffer+size; ptr>(unsigned short*)buffer; ) {
4109  const unsigned short val = *(--ptr);
4110  *ptr = (unsigned short)((val>>8)|((val<<8)));
4111  }
4112  } break;
4113  case 4 : { for (unsigned int *ptr = (unsigned int*)buffer+size; ptr>(unsigned int*)buffer; ) {
4114  const unsigned int val = *(--ptr);
4115  *ptr = (val>>24)|((val>>8)&0xff00)|((val<<8)&0xff0000)|(val<<24);
4116  }
4117  } break;
4118  default : { for (T* ptr = buffer+size; ptr>buffer; ) {
4119  unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
4120  for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
4121  }
4122  }
4123  }
4124  }
4125 
4127 
4131  template<typename T>
4132  inline T& invert_endianness(T& a) {
4133  invert_endianness(&a,1);
4134  return a;
4135  }
4136 
4137  // Conversion functions to get more precision when trying to store unsigned ints values as floats.
4138  inline unsigned int float2uint(const float f) {
4139  int tmp = 0;
4140  std::memcpy(&tmp,&f,sizeof(float));
4141  if (tmp>=0) return (unsigned int)f;
4142  unsigned int u;
4143  std::memcpy(&u,&f,sizeof(float)); // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
4144  return ((u)<<1)>>1; // set sign bit to 0.
4145  }
4146 
4147  inline float uint2float(const unsigned int u) {
4148  if (u<(1U<<19)) return (float)u; // Consider safe storage of unsigned int as floats until 19bits (i.e 524287).
4149  float f;
4150  const unsigned int v = u|(1U<<(8*sizeof(unsigned int)-1)); // set sign bit to 1.
4151  std::memcpy(&f,&v,sizeof(float)); // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
4152  return f;
4153  }
4154 
4156 
4159  inline unsigned long time() {
4160 #if cimg_OS==1
4161  struct timeval st_time;
4162  gettimeofday(&st_time,0);
4163  return (unsigned long)(st_time.tv_usec/1000 + st_time.tv_sec*1000);
4164 #elif cimg_OS==2
4165  SYSTEMTIME st_time;
4166  GetSystemTime(&st_time);
4167  return (unsigned long)(st_time.wMilliseconds + 1000*(st_time.wSecond + 60*(st_time.wMinute + 60*st_time.wHour)));
4168 #else
4169  return 0;
4170 #endif
4171  }
4172 
4173  // Implement a tic/toc mechanism to display elapsed time of algorithms.
4174  inline unsigned long tictoc(const bool is_tic) {
4175  static unsigned long t0 = 0;
4176  const unsigned long t = cimg::time();
4177  if (is_tic) return (t0 = t);
4178  const unsigned long dt = t>=t0?(t - t0):cimg::type<unsigned long>::max();
4179  const unsigned int
4180  edays = (unsigned int)(dt/86400000.0),
4181  ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0),
4182  emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0),
4183  esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0),
4184  ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0);
4185  if (!edays && !ehours && !emin && !esec)
4186  std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u ms%s\n",cimg::t_red,ems,cimg::t_normal);
4187  else {
4188  if (!edays && !ehours && !emin)
4189  std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u sec %u ms%s\n",cimg::t_red,esec,ems,cimg::t_normal);
4190  else {
4191  if (!edays && !ehours)
4192  std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u min %u sec %u ms%s\n",cimg::t_red,emin,esec,ems,cimg::t_normal);
4193  else{
4194  if (!edays)
4195  std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u hours %u min %u sec %u ms%s\n",cimg::t_red,ehours,emin,esec,ems,cimg::t_normal);
4196  else{
4197  std::fprintf(cimg::output(),"%s[CImg] Elapsed time: %u days %u hours %u min %u sec %u ms%s\n",cimg::t_red,edays,ehours,emin,esec,ems,cimg::t_normal);
4198  }
4199  }
4200  }
4201  }
4202  return t;
4203  }
4204 
4206 
4209  inline unsigned long tic() {
4210  return cimg::tictoc(true);
4211  }
4212 
4214 
4217  inline unsigned long toc() {
4218  return cimg::tictoc(false);
4219  }
4220 
4222 
4227  inline void sleep(const unsigned int milliseconds) {
4228 #if cimg_OS==1
4229  struct timespec tv;
4230  tv.tv_sec = milliseconds/1000;
4231  tv.tv_nsec = (milliseconds%1000)*1000000;
4232  nanosleep(&tv,0);
4233 #elif cimg_OS==2
4234  Sleep(milliseconds);
4235 #endif
4236  }
4237 
4238  inline unsigned int _wait(const unsigned int milliseconds, unsigned long& timer) {
4239  if (!timer) timer = cimg::time();
4240  const unsigned long current_time = cimg::time();
4241  if (current_time>=timer+milliseconds) { timer = current_time; return 0; }
4242  const unsigned long time_diff = timer + milliseconds - current_time;
4243  timer = current_time + time_diff;
4244  cimg::sleep(time_diff);
4245  return (unsigned int)time_diff;
4246  }
4247 
4249 
4255  inline unsigned int wait(const unsigned int milliseconds) {
4256  static unsigned long timer = 0;
4257  if (!timer) timer = cimg::time();
4258  return _wait(milliseconds,timer);
4259  }
4260 
4261  // Random number generators.
4262  // CImg may use its own Random Number Generator (RNG) if configuration macro 'cimg_use_rng' is set.
4263  // Use it for instance when you have to deal with concurrent threads trying to call std::srand()
4264  // at the same time!
4265 #ifdef cimg_use_rng
4266 
4267  // Use a custom RNG.
4268  inline unsigned int _rand(const unsigned long seed=0, const bool set_seed=false) {
4269  static unsigned long next = 1;
4270  if (set_seed) next = seed;
4271  next = next*1103515245 + 12345;
4272  return (unsigned int)((next>>16)&0x7FFF);
4273  }
4274 
4275  inline void srand() {
4276  static bool is_first_time = true;
4277  if (is_first_time) {
4278  const unsigned long t = cimg::time();
4279 #if cimg_OS==1
4280  cimg::_rand(t+(unsigned long)getpid(),true);
4281 #elif cimg_OS==2
4282  cimg::_rand(t+(unsigned long)_getpid(),true);
4283 #else
4284  cimg::_rand(t,true);
4285 #endif
4286  is_first_time = false;
4287  }
4288  }
4289 
4290  inline double rand() {
4291  cimg::srand();
4292  return cimg::_rand()/32767.;
4293  }
4294 
4295 #else
4296 
4297  // Use the system RNG.
4298  inline void srand() {
4299  static bool is_first_time = true;
4300  if (is_first_time) {
4301  const unsigned int t = (unsigned int)cimg::time();
4302 #if cimg_OS==1
4303  std::srand(t+(unsigned int)getpid());
4304 #elif cimg_OS==2
4305  std::srand(t+(unsigned int)_getpid());
4306 #else
4307  std::srand(t);
4308 #endif
4309  is_first_time = false;
4310  }
4311  }
4312 
4314 
4316  inline double rand() {
4317  cimg::srand();
4318  return (double)std::rand()/RAND_MAX;
4319  }
4320 #endif
4321 
4323 
4325  inline double crand() {
4326  return 1-2*cimg::rand();
4327  }
4328 
4330 
4332  inline double grand() {
4333  double x1, w;
4334  do {
4335  const double x2 = 2*cimg::rand() - 1.0;
4336  x1 = 2*cimg::rand()-1.0;
4337  w = x1*x1 + x2*x2;
4338  } while (w<=0 || w>=1.0);
4339  return x1*std::sqrt((-2*std::log(w))/w);
4340  }
4341 
4343 
4345  inline unsigned int prand(const double z) {
4346  if (z<=1.0e-10) return 0;
4347  if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand()) + z);
4348  unsigned int k = 0;
4349  const double y = std::exp(-z);
4350  for (double s = 1.0; s>=y; ++k) s*=cimg::rand();
4351  return k-1;
4352  }
4353 
4355  template<typename T>
4356  inline T rol(const T a, const unsigned int n=1) {
4357  return n?(T)((a<<n)|(a>>((sizeof(T)<<3)-n))):a;
4358  }
4359 
4360  inline float rol(const float a, const unsigned int n=1) {
4361  return (float)rol((int)a,n);
4362  }
4363 
4364  inline double rol(const double a, const unsigned int n=1) {
4365  return (double)rol((long)a,n);
4366  }
4367 
4369  template<typename T>
4370  inline T ror(const T a, const unsigned int n=1) {
4371  return n?(T)((a>>n)|(a<<((sizeof(T)<<3)-n))):a;
4372  }
4373 
4374  inline float ror(const float a, const unsigned int n=1) {
4375  return (float)ror((int)a,n);
4376  }
4377 
4378  inline double ror(const double a, const unsigned int n=1) {
4379  return (double)ror((long)a,n);
4380  }
4381 
4383  template<typename T>
4384  inline T abs(const T a) {
4385  return a>=0?a:-a;
4386  }
4387  inline bool abs(const bool a) {
4388  return a;
4389  }
4390  inline unsigned char abs(const unsigned char a) {
4391  return a;
4392  }
4393  inline unsigned short abs(const unsigned short a) {
4394  return a;
4395  }
4396  inline unsigned int abs(const unsigned int a) {
4397  return a;
4398  }
4399  inline unsigned long abs(const unsigned long a) {
4400  return a;
4401  }
4402  inline double abs(const double a) {
4403  return std::fabs(a);
4404  }
4405  inline float abs(const float a) {
4406  return (float)std::fabs((double)a);
4407  }
4408  inline int abs(const int a) {
4409  return std::abs(a);
4410  }
4411 
4413  template<typename T>
4414  inline T sqr(const T val) {
4415  return val*val;
4416  }
4417 
4419  inline int xln(const int x) {
4420  return x>0?(int)(1+std::log10((double)x)):1;
4421  }
4422 
4424  template<typename t1, typename t2>
4425  inline typename cimg::superset<t1,t2>::type min(const t1& a, const t2& b) {
4426  typedef typename cimg::superset<t1,t2>::type t1t2;
4427  return (t1t2)(a<=b?a:b);
4428  }
4429 
4431  template<typename t1, typename t2, typename t3>
4432  inline typename cimg::superset2<t1,t2,t3>::type min(const t1& a, const t2& b, const t3& c) {
4433  typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
4434  return (t1t2t3)cimg::min(cimg::min(a,b),c);
4435  }
4436 
4438  template<typename t1, typename t2, typename t3, typename t4>
4439  inline typename cimg::superset3<t1,t2,t3,t4>::type min(const t1& a, const t2& b, const t3& c, const t4& d) {
4440  typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
4441  return (t1t2t3t4)cimg::min(cimg::min(a,b,c),d);
4442  }
4443 
4445  template<typename t1, typename t2>
4446  inline typename cimg::superset<t1,t2>::type max(const t1& a, const t2& b) {
4447  typedef typename cimg::superset<t1,t2>::type t1t2;
4448  return (t1t2)(a>=b?a:b);
4449  }
4450 
4452  template<typename t1, typename t2, typename t3>
4453  inline typename cimg::superset2<t1,t2,t3>::type max(const t1& a, const t2& b, const t3& c) {
4454  typedef typename cimg::superset2<t1,t2,t3>::type t1t2t3;
4455  return (t1t2t3)cimg::max(cimg::max(a,b),c);
4456  }
4457 
4459  template<typename t1, typename t2, typename t3, typename t4>
4460  inline typename cimg::superset3<t1,t2,t3,t4>::type max(const t1& a, const t2& b, const t3& c, const t4& d) {
4461  typedef typename cimg::superset3<t1,t2,t3,t4>::type t1t2t3t4;
4462  return (t1t2t3t4)cimg::max(cimg::max(a,b,c),d);
4463  }
4464 
4466  template<typename T>
4467  inline T sign(const T x) {
4468  return (x<0)?(T)(-1):(x==0?(T)0:(T)1);
4469  }
4470 
4472  template<typename T>
4473  inline unsigned long nearest_pow2(const T x) {
4474  unsigned long i = 1;
4475  while (x>i) i<<=1;
4476  return i;
4477  }
4478 
4480  inline double sinc(const double x) {
4481  return x?std::sin(x)/x:1;
4482  }
4483 
4485 
4490  template<typename T>
4491  inline T mod(const T& x, const T& m) {
4492  const double dx = (double)x, dm = (double)m;
4493  return (T)(dx - dm * std::floor(dx / dm));
4494  }
4495  inline int mod(const bool x, const bool m) {
4496  return m?(x?1:0):0;
4497  }
4498  inline int mod(const char x, const char m) {
4499  return x>=0?x%m:(x%m?m+x%m:0);
4500  }
4501  inline int mod(const short x, const short m) {
4502  return x>=0?x%m:(x%m?m+x%m:0);
4503  }
4504  inline int mod(const int x, const int m) {
4505  return x>=0?x%m:(x%m?m+x%m:0);
4506  }
4507  inline int mod(const long x, const long m) {
4508  return x>=0?x%m:(x%m?m+x%m:0);
4509  }
4510  inline int mod(const unsigned char x, const unsigned char m) {
4511  return x%m;
4512  }
4513  inline int mod(const unsigned short x, const unsigned short m) {
4514  return x%m;
4515  }
4516  inline int mod(const unsigned int x, const unsigned int m) {
4517  return x%m;
4518  }
4519  inline int mod(const unsigned long x, const unsigned long m) {
4520  return x%m;
4521  }
4522 
4524 
4529  template<typename T>
4530  inline T minmod(const T a, const T b) {
4531  return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
4532  }
4533 
4535  inline double log2(const double x) {
4536  static const double base = std::log(2.0);
4537  return std::log(x)/base;
4538  }
4539 
4541 
4547  template<typename T>
4548  inline T round(const T x, const double y=1, const int rounding_type=0) {
4549  if (y<=0) return x;
4550  const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor;
4551  return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
4552  }
4553 
4554  inline double _pythagore(double a, double b) {
4555  const double absa = cimg::abs(a), absb = cimg::abs(b);
4556  if (absa>absb) { const double tmp = absb/absa; return absa*std::sqrt(1.0+tmp*tmp); }
4557  else { const double tmp = absa/absb; return absb==0?0:absb*std::sqrt(1.0+tmp*tmp); }
4558  }
4559 
4561  inline char uncase(const char x) {
4562  return (char)((x<'A'||x>'Z')?x:x-'A'+'a');
4563  }
4564 
4566  inline void uncase(char *const str) {
4567  if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uncase(*ptr);
4568  }
4569 
4571 
4576  inline double atof(const char *const str) {
4577  double x = 0, y = 1;
4578  if (!str) return 0; else { std::sscanf(str,"%lf/%lf",&x,&y); return x/y; }
4579  }
4580 
4582 
4589  inline int strncasecmp(const char *const str1, const char *const str2, const int l) {
4590  if (!l) return 0;
4591  if (!str1) return str2?-1:0;
4592  const char *nstr1 = str1, *nstr2 = str2;
4593  int k, diff = 0; for (k = 0; k<l && !(diff = uncase(*nstr1)-uncase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
4594  return k!=l?diff:0;
4595  }
4596 
4598 
4604  inline int strcasecmp(const char *const str1, const char *const str2) {
4605  if (!str1) return str2?-1:0;
4606  const unsigned int
4607  l1 = (unsigned int)std::strlen(str1),
4608  l2 = (unsigned int)std::strlen(str2);
4609  return cimg::strncasecmp(str1,str2,1+(l1<l2?l1:l2));
4610  }
4611 
4613 
4620  inline bool strpare(char *const str, const char delimiter=' ', const bool is_symmetric=false, const bool is_iterative=false) {
4621  if (!str) return false;
4622  const int l = (int)std::strlen(str);
4623  int p, q;
4624  if (is_symmetric) for (p = 0, q = l-1; p<q && str[p]==delimiter && str[q]==delimiter; ) { --q; ++p; if (!is_iterative) break; }
4625  else {
4626  for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
4627  for (q = l-1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
4628  }
4629  const int n = q - p + 1;
4630  if (n!=l) { std::memmove(str,str+p,n); str[n] = 0; return true; }
4631  return false;
4632  }
4633 
4635 
4638  inline void strescape(char *const str) {
4639 #define cimg_strescape(ci,co) case ci: *nd = co; ++ns; break;
4640  static unsigned int val = 0;
4641  for (char *ns = str, *nd = str; *ns || (bool)(*nd=0); ++nd) if (*ns=='\\') switch (*(++ns)) {
4642  cimg_strescape('n','\n');
4643  cimg_strescape('t','\t');
4644  cimg_strescape('v','\v');
4645  cimg_strescape('b','\b');
4646  cimg_strescape('r','\r');
4647  cimg_strescape('f','\f');
4648  cimg_strescape('a','\a');
4649  cimg_strescape('\\','\\');
4650  cimg_strescape('\?','\?');
4651  cimg_strescape('\'','\'');
4652  cimg_strescape('\"','\"');
4653  case 0 : *nd = 0; break;
4654  case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
4655  std::sscanf(ns,"%o",&val); while (*ns>='0' && *ns<='7') ++ns;
4656  *nd = val; break;
4657  case 'x':
4658  std::sscanf(++ns,"%x",&val); while ((*ns>='0' && *ns<='7') || (*ns>='a' && *ns<='f') || (*ns>='A' && *ns<='F')) ++ns;
4659  *nd = val; break;
4660  default : *nd = *(ns++);
4661  } else *nd = *(ns++);
4662  }
4663 
4664  // Return a temporary string describing the size of a memory buffer.
4665  inline const char *strbuffersize(const unsigned long size) {
4666  static char res[256] = { 0 };
4667  if (size<1024LU) cimg_snprintf(res,sizeof(res),"%lu byte%s",size,size>1?"s":"");
4668  else if (size<1024*1024LU) { const float nsize = size/1024.0f; cimg_snprintf(res,sizeof(res),"%.1f Kio",nsize); }
4669  else if (size<1024*1024*1024LU) { const float nsize = size/(1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Mio",nsize); }
4670  else { const float nsize = size/(1024*1024*1024.0f); cimg_snprintf(res,sizeof(res),"%.1f Gio",nsize); }
4671  return res;
4672  }
4673 
4675  inline const char* basename(const char *const str) {
4676  const char *p = 0;
4677  for (const char *np = str; np>=str && (p=np); np = std::strchr(np,cimg_file_separator)+1) {}
4678  return p;
4679  }
4680 
4681  // Return a random filename.
4682  inline const char* filenamerand() {
4683  static char randomid[9] = { 0,0,0,0,0,0,0,0,0 };
4684  cimg::srand();
4685  for (unsigned int k = 0; k<8; ++k) {
4686  const int v = (int)std::rand()%3;
4687  randomid[k] = (char)(v==0?('0'+(std::rand()%10)):(v==1?('a'+(std::rand()%26)):('A'+(std::rand()%26))));
4688  }
4689  return randomid;
4690  }
4691 
4692  // Convert filename as a Windows-style filename (short path name).
4693  inline void winformat_string(char *const str) {
4694  if (str && *str) {
4695 #if cimg_OS==2
4696  char *const nstr = new char[MAX_PATH];
4697  if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
4698 #endif
4699  }
4700  }
4701 
4703 
4710  inline std::FILE *fopen(const char *const path, const char *const mode) {
4711  if (!path)
4712  throw CImgArgumentException("cimg::fopen(): Specified file path is (null).");
4713  if (!mode)
4714  throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).",
4715  path);
4716  std::FILE *res = 0;
4717  if (*path=='-' && (!path[1] || path[1]=='.')) {
4718  res = (*mode=='r')?stdin:stdout;
4719 #if cimg_OS==2
4720  if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode.
4721  if (_setmode(_fileno(res),0x8000)==-1) res = 0;
4722  }
4723 #endif
4724  } else res = std::fopen(path,mode);
4725  if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.",
4726  path,mode);
4727  return res;
4728  }
4729 
4731 
4737  inline int fclose(std::FILE *file) {
4738  if (!file) warn("cimg::fclose(): Specified file is (null).");
4739  if (!file || file==stdin || file==stdout) return 0;
4740  const int errn = std::fclose(file);
4741  if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.",
4742  errn);
4743  return errn;
4744  }
4745 
4747 
4752  inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false) {
4753 #define _cimg_test_temporary_path(p) \
4754  if (!path_found) { \
4755  cimg_snprintf(st_path,1024,"%s",p); \
4756  cimg_snprintf(tmp,sizeof(tmp),"%s%c%s",st_path,cimg_file_separator,filetmp); \
4757  if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
4758  }
4759  static char *st_path = 0;
4760  if (reinit_path) { delete[] st_path; st_path = 0; }
4761  if (user_path) {
4762  if (!st_path) st_path = new char[1024];
4763  std::memset(st_path,0,1024);
4764  std::strncpy(st_path,user_path,1023);
4765  } else if (!st_path) {
4766  st_path = new char[1024];
4767  std::memset(st_path,0,1024);
4768  bool path_found = false;
4769  char tmp[1024] = { 0 }, filetmp[512] = { 0 };
4770  std::FILE *file = 0;
4771  cimg_snprintf(filetmp,sizeof(filetmp),"%s.tmp",cimg::filenamerand());
4772  char *tmpPath = std::getenv("TMP");
4773  if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
4774  if (tmpPath) _cimg_test_temporary_path(tmpPath);
4775 #if cimg_OS==2
4776  _cimg_test_temporary_path("C:\\WINNT\\Temp");
4777  _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
4778  _cimg_test_temporary_path("C:\\Temp");
4779  _cimg_test_temporary_path("C:");
4780  _cimg_test_temporary_path("D:\\WINNT\\Temp");
4781  _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
4782  _cimg_test_temporary_path("D:\\Temp");
4783  _cimg_test_temporary_path("D:");
4784 #else
4785  _cimg_test_temporary_path("/tmp");
4786  _cimg_test_temporary_path("/var/tmp");
4787 #endif
4788  if (!path_found) {
4789  *st_path = 0;
4790  std::strncpy(tmp,filetmp,sizeof(tmp)-1);
4791  if ((file=std::fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
4792  }
4793  if (!path_found)
4794  throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
4795  }
4796  return st_path;
4797  }
4798 
4800 
4805 #if cimg_OS==2
4806  inline const char* programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
4807  static char *st_path = 0;
4808  if (reinit_path) { delete[] st_path; st_path = 0; }
4809  if (user_path) {
4810  if (!st_path) st_path = new char[1024];
4811  std::memset(st_path,0,1024);
4812  std::strncpy(st_path,user_path,1023);
4813  } else if (!st_path) {
4814  st_path = new char[MAX_PATH];
4815  std::memset(st_path,0,MAX_PATH);
4816  // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
4817 #if !defined(__INTEL_COMPILER)
4818  if (!SHGetSpecialFolderPathA(0,st_path,0x0026,false)) {
4819  const char *const pfPath = std::getenv("PROGRAMFILES");
4820  if (pfPath) std::strncpy(st_path,pfPath,MAX_PATH-1);
4821  else std::strcpy(st_path,"C:\\PROGRA~1");
4822  }
4823 #else
4824  std::strcpy(st_path,"C:\\PROGRA~1");
4825 #endif
4826  }
4827  return st_path;
4828  }
4829 #endif
4830 
4832 
4837  inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false) {
4838  static char *st_path = 0;
4839  if (reinit_path) { delete[] st_path; st_path = 0; }
4840  if (user_path) {
4841  if (!st_path) st_path = new char[1024];
4842  std::memset(st_path,0,1024);
4843  std::strncpy(st_path,user_path,1023);
4844  } else if (!st_path) {
4845  st_path = new char[1024];
4846  std::memset(st_path,0,1024);
4847  bool path_found = false;
4848  std::FILE *file = 0;
4849 #if cimg_OS==2
4850  const char *const pf_path = programfiles_path();
4851  if (!path_found) {
4852  std::strcpy(st_path,".\\convert.exe");
4853  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4854  }
4855  for (int k = 32; k>=10 && !path_found; --k) {
4856  cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\convert.exe",pf_path,k);
4857  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4858  }
4859  for (int k = 9; k>=0 && !path_found; --k) {
4860  cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\convert.exe",pf_path,k);
4861  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4862  }
4863  for (int k = 32; k>=0 && !path_found; --k) {
4864  cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\convert.exe",pf_path,k);
4865  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4866  }
4867  for (int k = 32; k>=10 && !path_found; --k) {
4868  cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4869  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4870  }
4871  for (int k = 9; k>=0 && !path_found; --k) {
4872  cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4873  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4874  }
4875  for (int k = 32; k>=0 && !path_found; --k) {
4876  cimg_snprintf(st_path,sizeof(st_path),"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",pf_path,k);
4877  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4878  }
4879  for (int k = 32; k>=10 && !path_found; --k) {
4880  cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\convert.exe",k);
4881  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4882  }
4883  for (int k = 9; k>=0 && !path_found; --k) {
4884  cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\convert.exe",k);
4885  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4886  }
4887  for (int k = 32; k>=0 && !path_found; --k) {
4888  cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\convert.exe",k);
4889  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4890  }
4891  for (int k = 32; k>=10 && !path_found; --k) {
4892  cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
4893  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4894  }
4895  for (int k = 9; k>=0 && !path_found; --k) {
4896  cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
4897  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4898  }
4899  for (int k = 32; k>=0 && !path_found; --k) {
4900  cimg_snprintf(st_path,sizeof(st_path),"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
4901  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4902  }
4903  for (int k = 32; k>=10 && !path_found; --k) {
4904  cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\convert.exe",k);
4905  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4906  }
4907  for (int k = 9; k>=0 && !path_found; --k) {
4908  cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\convert.exe",k);
4909  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4910  }
4911  for (int k = 32; k>=0 && !path_found; --k) {
4912  cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\convert.exe",k);
4913  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4914  }
4915  for (int k = 32; k>=10 && !path_found; --k) {
4916  cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\convert.exe",k);
4917  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4918  }
4919  for (int k = 9; k>=0 && !path_found; --k) {
4920  cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\convert.exe",k);
4921  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4922  }
4923  for (int k = 32; k>=0 && !path_found; --k) {
4924  cimg_snprintf(st_path,sizeof(st_path),"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\convert.exe",k);
4925  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4926  }
4927  if (!path_found) std::strcpy(st_path,"convert.exe");
4928 #else
4929  if (!path_found) {
4930  std::strcpy(st_path,"./convert");
4931  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4932  }
4933  if (!path_found) std::strcpy(st_path,"convert");
4934 #endif
4935  winformat_string(st_path);
4936  }
4937  return st_path;
4938  }
4939 
4941 
4946  inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false) {
4947  static char *st_path = 0;
4948  if (reinit_path) { delete[] st_path; st_path = 0; }
4949  if (user_path) {
4950  if (!st_path) st_path = new char[1024];
4951  std::memset(st_path,0,1024);
4952  std::strncpy(st_path,user_path,1023);
4953  } else if (!st_path) {
4954  st_path = new char[1024];
4955  std::memset(st_path,0,1024);
4956  bool path_found = false;
4957  std::FILE *file = 0;
4958 #if cimg_OS==2
4959  const char *const pf_path = programfiles_path();
4960  if (!path_found) {
4961  std::strcpy(st_path,".\\gm.exe");
4962  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4963  }
4964  for (int k = 32; k>=10 && !path_found; --k) {
4965  cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
4966  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4967  }
4968  for (int k = 9; k>=0 && !path_found; --k) {
4969  cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
4970  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4971  }
4972  for (int k = 32; k>=0 && !path_found; --k) {
4973  cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
4974  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4975  }
4976  for (int k = 32; k>=10 && !path_found; --k) {
4977  cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4978  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4979  }
4980  for (int k = 9; k>=0 && !path_found; --k) {
4981  cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4982  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4983  }
4984  for (int k = 32; k>=0 && !path_found; --k) {
4985  cimg_snprintf(st_path,sizeof(st_path),"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
4986  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4987  }
4988  for (int k = 32; k>=10 && !path_found; --k) {
4989  cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
4990  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4991  }
4992  for (int k = 9; k>=0 && !path_found; --k) {
4993  cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
4994  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4995  }
4996  for (int k = 32; k>=0 && !path_found; --k) {
4997  cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\gm.exe",k);
4998  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
4999  }
5000  for (int k = 32; k>=10 && !path_found; --k) {
5001  cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
5002  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5003  }
5004  for (int k = 9; k>=0 && !path_found; --k) {
5005  cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
5006  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5007  }
5008  for (int k = 32; k>=0 && !path_found; --k) {
5009  cimg_snprintf(st_path,sizeof(st_path),"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
5010  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5011  }
5012  for (int k = 32; k>=10 && !path_found; --k) {
5013  cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
5014  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5015  }
5016  for (int k = 9; k>=0 && !path_found; --k) {
5017  cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
5018  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5019  }
5020  for (int k = 32; k>=0 && !path_found; --k) {
5021  cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\gm.exe",k);
5022  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5023  }
5024  for (int k = 32; k>=10 && !path_found; --k) {
5025  cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
5026  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5027  }
5028  for (int k = 9; k>=0 && !path_found; --k) {
5029  cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
5030  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5031  }
5032  for (int k = 32; k>=0 && !path_found; --k) {
5033  cimg_snprintf(st_path,sizeof(st_path),"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
5034  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5035  }
5036  if (!path_found) std::strcpy(st_path,"gm.exe");
5037 #else
5038  if (!path_found) {
5039  std::strcpy(st_path,"./gm");
5040  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5041  }
5042  if (!path_found) std::strcpy(st_path,"gm");
5043 #endif
5044  winformat_string(st_path);
5045  }
5046  return st_path;
5047  }
5048 
5050 
5055  inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false) {
5056  static char *st_path = 0;
5057  if (reinit_path) { delete[] st_path; st_path = 0; }
5058  if (user_path) {
5059  if (!st_path) st_path = new char[1024];
5060  std::memset(st_path,0,1024);
5061  std::strncpy(st_path,user_path,1023);
5062  } else if (!st_path) {
5063  st_path = new char[1024];
5064  std::memset(st_path,0,1024);
5065  bool path_found = false;
5066  std::FILE *file = 0;
5067 #if cimg_OS==2
5068  const char *const pf_path = programfiles_path();
5069  if (!path_found) {
5070  std::strcpy(st_path,".\\medcon.exe");
5071  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5072  }
5073  if (!path_found) {
5074  cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.bat",pf_path);
5075  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5076  }
5077  if (!path_found) {
5078  cimg_snprintf(st_path,sizeof(st_path),"%s\\XMedCon\\bin\\medcon.exe",pf_path);
5079  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5080  }
5081  if (!path_found) std::strcpy(st_path,"medcon.exe");
5082 #else
5083  if (!path_found) {
5084  std::strcpy(st_path,"./medcon");
5085  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5086  }
5087  if (!path_found) std::strcpy(st_path,"medcon");
5088 #endif
5089  winformat_string(st_path);
5090  }
5091  return st_path;
5092  }
5093 
5095 
5100  inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false) {
5101  static char *st_path = 0;
5102  if (reinit_path) { delete[] st_path; st_path = 0; }
5103  if (user_path) {
5104  if (!st_path) st_path = new char[1024];
5105  std::memset(st_path,0,1024);
5106  std::strncpy(st_path,user_path,1023);
5107  } else if (!st_path) {
5108  st_path = new char[1024];
5109  std::memset(st_path,0,1024);
5110  bool path_found = false;
5111  std::FILE *file = 0;
5112 #if cimg_OS==2
5113  if (!path_found) {
5114  std::strcpy(st_path,".\\ffmpeg.exe");
5115  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5116  }
5117  if (!path_found) std::strcpy(st_path,"ffmpeg.exe");
5118 #else
5119  if (!path_found) {
5120  std::strcpy(st_path,"./ffmpeg");
5121  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5122  }
5123  if (!path_found) std::strcpy(st_path,"ffmpeg");
5124 #endif
5125  winformat_string(st_path);
5126  }
5127  return st_path;
5128  }
5129 
5131 
5136  inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false) {
5137  static char *st_path = 0;
5138  if (reinit_path) { delete[] st_path; st_path = 0; }
5139  if (user_path) {
5140  if (!st_path) st_path = new char[1024];
5141  std::memset(st_path,0,1024);
5142  std::strncpy(st_path,user_path,1023);
5143  } else if (!st_path) {
5144  st_path = new char[1024];
5145  std::memset(st_path,0,1024);
5146  bool path_found = false;
5147  std::FILE *file = 0;
5148 #if cimg_OS==2
5149  if (!path_found) {
5150  std::strcpy(st_path,".\\gzip.exe");
5151  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5152  }
5153  if (!path_found) std::strcpy(st_path,"gzip.exe");
5154 #else
5155  if (!path_found) {
5156  std::strcpy(st_path,"./gzip");
5157  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5158  }
5159  if (!path_found) std::strcpy(st_path,"gzip");
5160 #endif
5161  winformat_string(st_path);
5162  }
5163  return st_path;
5164  }
5165 
5167 
5172  inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false) {
5173  static char *st_path = 0;
5174  if (reinit_path) { delete[] st_path; st_path = 0; }
5175  if (user_path) {
5176  if (!st_path) st_path = new char[1024];
5177  std::memset(st_path,0,1024);
5178  std::strncpy(st_path,user_path,1023);
5179  } else if (!st_path) {
5180  st_path = new char[1024];
5181  std::memset(st_path,0,1024);
5182  bool path_found = false;
5183  std::FILE *file = 0;
5184 #if cimg_OS==2
5185  if (!path_found) {
5186  std::strcpy(st_path,".\\gunzip.exe");
5187  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5188  }
5189  if (!path_found) std::strcpy(st_path,"gunzip.exe");
5190 #else
5191  if (!path_found) {
5192  std::strcpy(st_path,"./gunzip");
5193  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5194  }
5195  if (!path_found) std::strcpy(st_path,"gunzip");
5196 #endif
5197  winformat_string(st_path);
5198  }
5199  return st_path;
5200  }
5201 
5203 
5208  inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false) {
5209  static char *st_path = 0;
5210  if (reinit_path) { delete[] st_path; st_path = 0; }
5211  if (user_path) {
5212  if (!st_path) st_path = new char[1024];
5213  std::memset(st_path,0,1024);
5214  std::strncpy(st_path,user_path,1023);
5215  } else if (!st_path) {
5216  st_path = new char[1024];
5217  std::memset(st_path,0,1024);
5218  bool path_found = false;
5219  std::FILE *file = 0;
5220 #if cimg_OS==2
5221  if (!path_found) {
5222  std::strcpy(st_path,".\\dcraw.exe");
5223  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5224  }
5225  if (!path_found) std::strcpy(st_path,"dcraw.exe");
5226 #else
5227  if (!path_found) {
5228  std::strcpy(st_path,"./dcraw");
5229  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5230  }
5231  if (!path_found) std::strcpy(st_path,"dcraw");
5232 #endif
5233  winformat_string(st_path);
5234  }
5235  return st_path;
5236  }
5237 
5239 
5244  inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false) {
5245  static char *st_path = 0;
5246  if (reinit_path) { delete[] st_path; st_path = 0; }
5247  if (user_path) {
5248  if (!st_path) st_path = new char[1024];
5249  std::memset(st_path,0,1024);
5250  std::strncpy(st_path,user_path,1023);
5251  } else if (!st_path) {
5252  st_path = new char[1024];
5253  std::memset(st_path,0,1024);
5254  bool path_found = false;
5255  std::FILE *file = 0;
5256 #if cimg_OS==2
5257  if (!path_found) {
5258  std::strcpy(st_path,".\\wget.exe");
5259  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5260  }
5261  if (!path_found) std::strcpy(st_path,"wget.exe");
5262 #else
5263  if (!path_found) {
5264  std::strcpy(st_path,"./wget");
5265  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5266  }
5267  if (!path_found) std::strcpy(st_path,"wget");
5268 #endif
5269  winformat_string(st_path);
5270  }
5271  return st_path;
5272  }
5273 
5275 
5280  inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false) {
5281  static char *st_path = 0;
5282  if (reinit_path) { delete[] st_path; st_path = 0; }
5283  if (user_path) {
5284  if (!st_path) st_path = new char[1024];
5285  std::memset(st_path,0,1024);
5286  std::strncpy(st_path,user_path,1023);
5287  } else if (!st_path) {
5288  st_path = new char[1024];
5289  std::memset(st_path,0,1024);
5290  bool path_found = false;
5291  std::FILE *file = 0;
5292 #if cimg_OS==2
5293  if (!path_found) {
5294  std::strcpy(st_path,".\\curl.exe");
5295  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5296  }
5297  if (!path_found) std::strcpy(st_path,"curl.exe");
5298 #else
5299  if (!path_found) {
5300  std::strcpy(st_path,"./curl");
5301  if ((file=std::fopen(st_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
5302  }
5303  if (!path_found) std::strcpy(st_path,"curl");
5304 #endif
5305  winformat_string(st_path);
5306  }
5307  return st_path;
5308  }
5309 
5311  inline const char *split_filename(const char *const filename, char *const body=0) {
5312  if (!filename) { if (body) *body = 0; return 0; }
5313  const char *p = 0; for (const char *np = filename; np>=filename && (p=np); np = std::strchr(np,'.')+1) {}
5314  if (p==filename) {
5315  if (body) std::strcpy(body,filename);
5316  return filename + std::strlen(filename);
5317  }
5318  const unsigned int l = (unsigned int)(p - filename - 1);
5319  if (body) { std::memcpy(body,filename,l); body[l] = 0; }
5320  return p;
5321  }
5322 
5324  inline char* number_filename(const char *const filename, const int number, const unsigned int n, char *const str) {
5325  if (!filename) { if (str) *str = 0; return 0; }
5326  char format[1024] = { 0 }, body[1024] = { 0 };
5327  const char *const ext = cimg::split_filename(filename,body);
5328  if (n>0) cimg_snprintf(format,sizeof(format),"%s_%%.%ud.%s",body,n,ext);
5329  else cimg_snprintf(format,sizeof(format),"%s_%%d.%s",body,ext);
5330  std::sprintf(str,format,number);
5331  return str;
5332  }
5333 
5335 
5340  inline const char *file_type(std::FILE *const file, const char *const filename) {
5341  if (!file && !filename)
5342  throw CImgArgumentException("cimg::file_type(): Specified filename is (null).");
5343  static const char
5344  *const _pnm = "pnm",
5345  *const _pfm = "pfm",
5346  *const _bmp = "bmp",
5347  *const _gif = "gif",
5348  *const _jpg = "jpg",
5349  *const _off = "off",
5350  *const _pan = "pan",
5351  *const _png = "png",
5352  *const _tif = "tif",
5353  *const _inr = "inr",
5354  *const _dcm = "dcm";
5355  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
5356  const char *f_type = 0, *head;
5357  char header[2048] = { 0 }, item[1024] = { 0 };
5358  const unsigned char *const uheader = (unsigned char*)header;
5359  int err; char cerr;
5360  const unsigned int siz = (unsigned int)std::fread(header,2048,1,nfile); // Read first 2048 bytes.
5361  if (!file) cimg::fclose(nfile);
5362 
5363  if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // Check for OFF format.
5364  else if (!std::strncmp(header,"#INRIMAGE",9)) f_type = _inr; // Check for INRIMAGE format.
5365  else if (!std::strncmp(header,"PANDORE",7)) f_type = _pan; // Check for PANDORE format.
5366  else if (!std::strncmp(header+128,"DICM",4)) f_type = _dcm; // Check for DICOM format.
5367  else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) f_type = _jpg; // Check for JPEG format.
5368  else if (header[0]=='B' && header[1]=='M') f_type = _bmp; // Check for BMP format.
5369  else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' && // Check for GIF format.
5370  (header[4]=='7' || header[4]=='9')) f_type = _gif;
5371  else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 && // Check for PNG format.
5372  uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) f_type = _png;
5373  else if ((uheader[0]==0x49 && uheader[1]==0x49) || (uheader[0]==0x4D && uheader[1]==0x4D)) f_type = _tif; // Check for TIFF format.
5374  else { // Check for PNM or PFM format.
5375  head = header;
5376  while (head<header+siz && (err=std::sscanf(head,"%1023[^\n]",item))!=EOF && (*item=='#' || !err))
5377  head+=1+(err?std::strlen(item):0);
5378  if (std::sscanf(item," P%d",&err)==1) f_type = _pnm;
5379  else if (std::sscanf(item," P%c",&cerr)==1 && (cerr=='f' || cerr=='F')) f_type = _pfm;
5380  }
5381  return f_type;
5382  }
5383 
5385 
5392  template<typename T>
5393  inline int fread(T *const ptr, const unsigned long nmemb, std::FILE *stream) {
5394  if (!ptr || nmemb<=0 || !stream)
5395  throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.",
5396  nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
5397 
5398  const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
5399  unsigned long to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
5400  do {
5401  l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
5402  l_al_read = (unsigned long)std::fread((void*)(ptr+al_read),sizeof(T),l_to_read,stream);
5403  al_read+=l_al_read;
5404  to_read-=l_al_read;
5405  } while (l_to_read==l_al_read && to_read>0);
5406  if (to_read>0)
5407  warn("cimg::fread(): Only %u/%u elements could be read from file.",
5408  al_read,nmemb);
5409  return al_read;
5410  }
5411 
5413 
5420  template<typename T>
5421  inline int fwrite(const T *ptr, const unsigned long nmemb, std::FILE *stream) {
5422  if (!ptr || !stream)
5423  throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.",
5424  nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
5425  if (nmemb<=0) return 0;
5426  const unsigned long wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
5427  unsigned long to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
5428  do {
5429  l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
5430  l_al_write = (unsigned long)std::fwrite((void*)(ptr+al_write),sizeof(T),l_to_write,stream);
5431  al_write+=l_al_write;
5432  to_write-=l_al_write;
5433  } while (l_to_write==l_al_write && to_write>0);
5434  if (to_write>0)
5435  warn("cimg::fwrite(): Only %u/%u elements could be written in file.",
5436  al_write,nmemb);
5437  return al_write;
5438  }
5439 
5441 
5448  inline char *load_network_external(const char *const filename, char *const filename_local) {
5449  if (!filename)
5450  throw CImgArgumentException("cimg::load_network_external(): Specified filename is (null).");
5451  if (!filename_local)
5452  throw CImgArgumentException("cimg::load_network_external(): Specified destination string is (null).");
5453  const char *const _ext = cimg::split_filename(filename), *const ext = (*_ext && _ext>filename)?_ext-1:_ext;
5454  char command[1024] = { 0 };
5455  std::FILE *file = 0;
5456  *filename_local = 0;
5457  do {
5458  cimg_snprintf(filename_local,512,"%s%c%s%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
5459  if ((file=std::fopen(filename_local,"rb"))!=0) cimg::fclose(file);
5460  } while (file);
5461 
5462  // Try with 'curl' first.
5463  cimg_snprintf(command,sizeof(command),"%s -f --silent --compressed -o \"%s\" \"%s\"",cimg::curl_path(),filename_local,filename);
5464  cimg::system(command);
5465  if (!(file = std::fopen(filename_local,"rb"))) {
5466 
5467  // Try with 'wget' else.
5468  cimg_snprintf(command,sizeof(command),"%s -q -r -l 0 --no-cache -O \"%s\" \"%s\"",cimg::wget_path(),filename_local,filename);
5469  cimg::system(command);
5470  if (!(file = std::fopen(filename_local,"rb")))
5471  throw CImgIOException("cimg::load_network_external(): Failed to load file '%s' with external tools 'wget' or 'curl'.",filename);
5472  cimg::fclose(file);
5473 
5474  // Try gunzip it.
5475  cimg_snprintf(command,sizeof(command),"%s.gz",filename_local);
5476  std::rename(filename_local,command);
5477  cimg_snprintf(command,sizeof(command),"%s --quiet %s.gz",gunzip_path(),filename_local);
5478  cimg::system(command);
5479  file = std::fopen(filename_local,"rb");
5480  if (!file) {
5481  cimg_snprintf(command,sizeof(command),"%s.gz",filename_local);
5482  std::rename(command,filename_local);
5483  file = std::fopen(filename_local,"rb");
5484  }
5485  }
5486  std::fseek(file,0,SEEK_END); // Check if file size is 0.
5487  if (std::ftell(file)<=0)
5488  throw CImgIOException("cimg::load_network_external(): Failed to load file '%s' with external commands 'wget' or 'curl'.",filename);
5489  cimg::fclose(file);
5490  return filename_local;
5491  }
5492 
5494  inline const char* option(const char *const name, const int argc, const char *const *const argv,
5495  const char *const defaut, const char *const usage, const bool reset_static) {
5496  static bool first = true, visu = false;
5497  if (reset_static) { first = true; return 0; }
5498  const char *res = 0;
5499  if (first) {
5500  first = false;
5501  visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
5502  visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
5503  visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
5504  }
5505  if (!name && visu) {
5506  if (usage) {
5507  std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
5508  std::fprintf(cimg::output(),": %s",usage);
5509  std::fprintf(cimg::output()," (%s, %s)\n\n",__DATE__,__TIME__);
5510  }
5511  if (defaut) std::fprintf(cimg::output(),"%s\n",defaut);
5512  }
5513  if (name) {
5514  if (argc>0) {
5515  int k = 0;
5516  while (k<argc && std::strcmp(argv[k],name)) ++k;
5517  res = (k++==argc?defaut:(k==argc?argv[--k]:argv[k]));
5518  } else res = defaut;
5519  if (visu && usage) std::fprintf(cimg::output()," %s%-16s%s %-24s %s%s%s\n",
5520  cimg::t_bold,name,cimg::t_normal,res?res:"0",cimg::t_green,usage,cimg::t_normal);
5521  }
5522  return res;
5523  }
5524 
5525  inline const char* option(const char *const name, const int argc, const char *const *const argv,
5526  const char *const defaut, const char *const usage=0) {
5527  return option(name,argc,argv,defaut,usage,false);
5528  }
5529 
5530  inline bool option(const char *const name, const int argc, const char *const *const argv,
5531  const bool defaut, const char *const usage=0) {
5532  const char *const s = cimg::option(name,argc,argv,(char*)0);
5533  const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):defaut;
5534  cimg::option(name,0,0,res?"true":"false",usage);
5535  return res;
5536  }
5537 
5538  inline int option(const char *const name, const int argc, const char *const *const argv,
5539  const int defaut, const char *const usage=0) {
5540  const char *const s = cimg::option(name,argc,argv,(char*)0);
5541  const int res = s?std::atoi(s):defaut;
5542  char tmp[256] = { 0 };
5543  cimg_snprintf(tmp,sizeof(tmp),"%d",res);
5544  cimg::option(name,0,0,tmp,usage);
5545  return res;
5546  }
5547 
5548  inline char option(const char *const name, const int argc, const char *const *const argv,
5549  const char defaut, const char *const usage=0) {
5550  const char *const s = cimg::option(name,argc,argv,(char*)0);
5551  const char res = s?*s:defaut;
5552  char tmp[8] = { 0 };
5553  *tmp = res;
5554  cimg::option(name,0,0,tmp,usage);
5555  return res;
5556  }
5557 
5558  inline float option(const char *const name, const int argc, const char *const *const argv,
5559  const float defaut, const char *const usage=0) {
5560  const char *const s = cimg::option(name,argc,argv,(char*)0);
5561  const float res = s?(float)cimg::atof(s):defaut;
5562  char tmp[256] = { 0 };
5563  cimg_snprintf(tmp,sizeof(tmp),"%g",res);
5564  cimg::option(name,0,0,tmp,usage);
5565  return res;
5566  }
5567 
5568  inline double option(const char *const name, const int argc, const char *const *const argv,
5569  const double defaut, const char *const usage=0) {
5570  const char *const s = cimg::option(name,argc,argv,(char*)0);
5571  const double res = s?cimg::atof(s):defaut;
5572  char tmp[256] = { 0 };
5573  cimg_snprintf(tmp,sizeof(tmp),"%g",res);
5574  cimg::option(name,0,0,tmp,usage);
5575  return res;
5576  }
5577 
5578  inline const char* argument(const unsigned int nb, const int argc, const char *const *const argv, const unsigned int nb_singles=0, ...) {
5579  for (int k = 1, pos = 0; k<argc;) {
5580  const char *const item = argv[k];
5581  bool option = (*item=='-'), single_option = false;
5582  if (option) {
5583  va_list ap;
5584  va_start(ap,nb_singles);
5585  for (unsigned int i = 0; i<nb_singles; ++i) if (!cimg::strcasecmp(item,va_arg(ap,char*))) { single_option = true; break; }
5586  va_end(ap);
5587  }
5588  if (option) { ++k; if (!single_option) ++k; }
5589  else { if (pos++==(int)nb) return item; else ++k; }
5590  }
5591  return 0;
5592  }
5593 
5595 
5598  inline void info() {
5599  char tmp[1024] = { 0 };
5600  std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n",
5601  cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
5602  cimg::t_normal,__DATE__,__TIME__);
5603 
5604  std::fprintf(cimg::output()," > Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n",
5605  cimg::t_bold,
5606  cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknow"),
5607  cimg::t_normal,cimg::t_green,
5608  cimg_OS,
5609  cimg::t_normal);
5610 
5611  std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n",
5612  cimg::t_bold,
5613  cimg::endianness()?"Big":"Little",
5614  cimg::t_normal);
5615 
5616  std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
5617  cimg::t_bold,
5618  cimg_verbosity==0?"Quiet":(cimg_verbosity==1?"Console":(cimg_verbosity==2?"Dialog":(cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings"))),
5619  cimg::t_normal,cimg::t_green,
5620  cimg_verbosity,
5621  cimg::t_normal);
5622 
5623  std::fprintf(cimg::output()," > Stricts warnings: %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
5624  cimg::t_bold,
5625 #ifdef cimg_strict_warnings
5626  "Yes",cimg::t_normal,cimg::t_green,"defined",
5627 #else
5628  "No",cimg::t_normal,cimg::t_green,"undefined",
5629 #endif
5630  cimg::t_normal);
5631 
5632  std::fprintf(cimg::output()," > Using VT100 messages: %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
5633  cimg::t_bold,
5634 #ifdef cimg_use_vt100
5635  "Yes",cimg::t_normal,cimg::t_green,"defined",
5636 #else
5637  "No",cimg::t_normal,cimg::t_green,"undefined",
5638 #endif
5639  cimg::t_normal);
5640 
5641  std::fprintf(cimg::output()," > Display type: %s%-13s%s %s('cimg_display'=%d)%s\n",
5642  cimg::t_bold,
5643  cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
5644  cimg::t_normal,cimg::t_green,
5645  cimg_display,
5646  cimg::t_normal);
5647 
5648 #if cimg_display==1
5649  std::fprintf(cimg::output()," > Using XShm for X11: %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
5650  cimg::t_bold,
5651 #ifdef cimg_use_xshm
5652  "Yes",cimg::t_normal,cimg::t_green,"defined",
5653 #else
5654  "No",cimg::t_normal,cimg::t_green,"undefined",
5655 #endif
5656  cimg::t_normal);
5657 
5658  std::fprintf(cimg::output()," > Using XRand for X11: %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
5659  cimg::t_bold,
5660 #ifdef cimg_use_xrandr
5661  "Yes",cimg::t_normal,cimg::t_green,"defined",
5662 #else
5663  "No",cimg::t_normal,cimg::t_green,"undefined",
5664 #endif
5665  cimg::t_normal);
5666 #endif
5667  std::fprintf(cimg::output()," > Using OpenMP: %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
5668  cimg::t_bold,
5669 #ifdef cimg_use_openmp
5670  "Yes",cimg::t_normal,cimg::t_green,"defined",
5671 #else
5672  "No",cimg::t_normal,cimg::t_green,"undefined",
5673 #endif
5674  cimg::t_normal);
5675  std::fprintf(cimg::output()," > Using PNG library: %s%-13s%s %s('cimg_use_png' %s)%s\n",
5676  cimg::t_bold,
5677 #ifdef cimg_use_png
5678  "Yes",cimg::t_normal,cimg::t_green,"defined",
5679 #else
5680  "No",cimg::t_normal,cimg::t_green,"undefined",
5681 #endif
5682  cimg::t_normal);
5683  std::fprintf(cimg::output()," > Using JPEG library: %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
5684  cimg::t_bold,
5685 #ifdef cimg_use_jpeg
5686  "Yes",cimg::t_normal,cimg::t_green,"defined",
5687 #else
5688  "No",cimg::t_normal,cimg::t_green,"undefined",
5689 #endif
5690  cimg::t_normal);
5691 
5692  std::fprintf(cimg::output()," > Using TIFF library: %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
5693  cimg::t_bold,
5694 #ifdef cimg_use_tiff
5695  "Yes",cimg::t_normal,cimg::t_green,"defined",
5696 #else
5697  "No",cimg::t_normal,cimg::t_green,"undefined",
5698 #endif
5699  cimg::t_normal);
5700 
5701  std::fprintf(cimg::output()," > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n",
5702  cimg::t_bold,
5703 #ifdef cimg_use_magick
5704  "Yes",cimg::t_normal,cimg::t_green,"defined",
5705 #else
5706  "No",cimg::t_normal,cimg::t_green,"undefined",
5707 #endif
5708  cimg::t_normal);
5709 
5710  std::fprintf(cimg::output()," > Using FFTW3 library: %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
5711  cimg::t_bold,
5712 #ifdef cimg_use_fftw3
5713  "Yes",cimg::t_normal,cimg::t_green,"defined",
5714 #else
5715  "No",cimg::t_normal,cimg::t_green,"undefined",
5716 #endif
5717  cimg::t_normal);
5718 
5719  std::fprintf(cimg::output()," > Using LAPACK library: %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
5720  cimg::t_bold,
5721 #ifdef cimg_use_lapack
5722  "Yes",cimg::t_normal,cimg::t_green,"defined",
5723 #else
5724  "No",cimg::t_normal,cimg::t_green,"undefined",
5725 #endif
5726  cimg::t_normal);
5727 
5728  cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::imagemagick_path());
5729  std::fprintf(cimg::output()," > Path of ImageMagick: %s%-13s%s\n",
5730  cimg::t_bold,
5731  tmp,
5732  cimg::t_normal);
5733 
5734  cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::graphicsmagick_path());
5735  std::fprintf(cimg::output()," > Path of GraphicsMagick: %s%-13s%s\n",
5736  cimg::t_bold,
5737  tmp,
5738  cimg::t_normal);
5739 
5740  cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::medcon_path());
5741  std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n",
5742  cimg::t_bold,
5743  tmp,
5744  cimg::t_normal);
5745 
5746  cimg_snprintf(tmp,sizeof(tmp),"\"%.1020s\"",cimg::temporary_path());
5747  std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n",
5748  cimg::t_bold,
5749  tmp,
5750  cimg::t_normal);
5751 
5752  std::fprintf(cimg::output(),"\n");
5753  }
5754 
5755  // Declare LAPACK function signatures if LAPACK support is enabled.
5756 #ifdef cimg_use_lapack
5757  template<typename T>
5758  inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
5759  dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
5760  }
5761 
5762  inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
5763  sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
5764  }
5765 
5766  template<typename T>
5767  inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
5768  dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
5769  }
5770 
5771  inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
5772  sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
5773  }
5774 
5775  template<typename T>
5776  inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
5777  T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
5778  dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
5779  }
5780 
5781  inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
5782  float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
5783  sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
5784  }
5785 
5786  template<typename T>
5787  inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
5788  int one = 1;
5789  dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
5790  }
5791 
5792  inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
5793  int one = 1;
5794  sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
5795  }
5796 
5797  template<typename T>
5798  inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
5799  dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
5800  }
5801 
5802  inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
5803  ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
5804  }
5805 
5806  template<typename T>
5807  inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
5808  T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO){
5809  dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
5810  }
5811 
5812  inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
5813  float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO){
5814  sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
5815  }
5816 
5817 #endif
5818 
5819  // End of the 'cimg' namespace
5820  }
5821 
5822  /*------------------------------------------------
5823  #
5824  #
5825  # Definition of mathematical operators and
5826  # external functions.
5827  #
5828  #
5829  -------------------------------------------------*/
5830 
5831 #define _cimg_create_ext_operators(typ) \
5832  template<typename T> \
5833  inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
5834  return img + val; \
5835  } \
5836  template<typename T> \
5837  inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
5838  typedef typename cimg::superset<T,typ>::type Tt; \
5839  return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
5840  } \
5841  template<typename T> \
5842  inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
5843  return img*val; \
5844  } \
5845  template<typename T> \
5846  inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
5847  return val*img.get_invert(); \
5848  } \
5849  template<typename T> \
5850  inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
5851  return img & val; \
5852  } \
5853  template<typename T> \
5854  inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
5855  return img | val; \
5856  } \
5857  template<typename T> \
5858  inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
5859  return img ^ val; \
5860  } \
5861  template<typename T> \
5862  inline bool operator==(const typ val, const CImg<T>& img) { \
5863  return img == val; \
5864  } \
5865  template<typename T> \
5866  inline bool operator!=(const typ val, const CImg<T>& img) { \
5867  return img != val; \
5868  }
5869 
5870  _cimg_create_ext_operators(bool)
5871  _cimg_create_ext_operators(unsigned char)
5872  _cimg_create_ext_operators(char)
5873  _cimg_create_ext_operators(signed char)
5874  _cimg_create_ext_operators(unsigned short)
5875  _cimg_create_ext_operators(short)
5876  _cimg_create_ext_operators(unsigned int)
5877  _cimg_create_ext_operators(int)
5878  _cimg_create_ext_operators(unsigned long)
5879  _cimg_create_ext_operators(long)
5880  _cimg_create_ext_operators(float)
5881  _cimg_create_ext_operators(double)
5882 
5883  template<typename T>
5884  inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
5885  return img + expression;
5886  }
5887 
5888  template<typename T>
5889  inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
5890  return CImg<_cimg_Tfloat>(img._width,img._height,img._depth,img._spectrum,expression,true)-=img;
5891  }
5892 
5893  template<typename T>
5894  inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
5895  return img*expression;
5896  }
5897 
5898  template<typename T>
5899  inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
5900  return expression*img.get_invert();
5901  }
5902 
5903  template<typename T>
5904  inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
5905  return img & expression;
5906  }
5907 
5908  template<typename T>
5909  inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
5910  return img | expression;
5911  }
5912 
5913  template<typename T>
5914  inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
5915  return img ^ expression;
5916  }
5917 
5918  template<typename T>
5919  inline bool operator==(const char *const expression, const CImg<T>& img) {
5920  return img == expression;
5921  }
5922 
5923  template<typename T>
5924  inline bool operator!=(const char *const expression, const CImg<T>& img) {
5925  return img != expression;
5926  }
5927 
5928  template<typename T>
5929  inline CImg<_cimg_Tfloat> sqr(const CImg<T>& instance) {
5930  return instance.get_sqr();
5931  }
5932 
5933  template<typename T>
5934  inline CImg<_cimg_Tfloat> sqrt(const CImg<T>& instance) {
5935  return instance.get_sqrt();
5936  }
5937 
5938  template<typename T>
5939  inline CImg<_cimg_Tfloat> exp(const CImg<T>& instance) {
5940  return instance.get_exp();
5941  }
5942 
5943  template<typename T>
5944  inline CImg<_cimg_Tfloat> log(const CImg<T>& instance) {
5945  return instance.get_log();
5946  }
5947 
5948  template<typename T>
5949  inline CImg<_cimg_Tfloat> log2(const CImg<T>& instance) {
5950  return instance.get_log2();
5951  }
5952 
5953  template<typename T>
5954  inline CImg<_cimg_Tfloat> log10(const CImg<T>& instance) {
5955  return instance.get_log10();
5956  }
5957 
5958  template<typename T>
5959  inline CImg<_cimg_Tfloat> abs(const CImg<T>& instance) {
5960  return instance.get_abs();
5961  }
5962 
5963  template<typename T>
5964  inline CImg<_cimg_Tfloat> sign(const CImg<T>& instance) {
5965  return instance.get_sign();
5966  }
5967 
5968  template<typename T>
5969  inline CImg<_cimg_Tfloat> cos(const CImg<T>& instance) {
5970  return instance.get_cos();
5971  }
5972 
5973  template<typename T>
5974  inline CImg<_cimg_Tfloat> sin(const CImg<T>& instance) {
5975  return instance.get_sin();
5976  }
5977 
5978  template<typename T>
5979  inline CImg<_cimg_Tfloat> sinc(const CImg<T>& instance) {
5980  return instance.get_sinc();
5981  }
5982 
5983  template<typename T>
5984  inline CImg<_cimg_Tfloat> tan(const CImg<T>& instance) {
5985  return instance.get_tan();
5986  }
5987 
5988  template<typename T>
5989  inline CImg<_cimg_Tfloat> acos(const CImg<T>& instance) {
5990  return instance.get_acos();
5991  }
5992 
5993  template<typename T>
5994  inline CImg<_cimg_Tfloat> asin(const CImg<T>& instance) {
5995  return instance.get_asin();
5996  }
5997 
5998  template<typename T>
5999  inline CImg<_cimg_Tfloat> atan(const CImg<T>& instance) {
6000  return instance.get_atan();
6001  }
6002 
6003  template<typename T>
6004  inline CImg<_cimg_Tfloat> cosh(const CImg<T>& instance) {
6005  return instance.get_cosh();
6006  }
6007 
6008  template<typename T>
6009  inline CImg<_cimg_Tfloat> sinh(const CImg<T>& instance) {
6010  return instance.get_sinh();
6011  }
6012 
6013  template<typename T>
6014  inline CImg<_cimg_Tfloat> tanh(const CImg<T>& instance) {
6015  return instance.get_tanh();
6016  }
6017 
6018  template<typename T>
6019  inline CImg<T> transpose(const CImg<T>& instance) {
6020  return instance.get_transpose();
6021  }
6022 
6023  template<typename T>
6024  inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance) {
6025  return instance.get_invert();
6026  }
6027 
6028  template<typename T>
6029  inline CImg<_cimg_Tfloat> pseudoinvert(const CImg<T>& instance) {
6030  return instance.get_pseudoinvert();
6031  }
6032 
6033  /*-----------------------------------
6034  #
6035  # Define the CImgDisplay structure
6036  #
6037  ----------------------------------*/
6039 
6054  struct CImgDisplay {
6055  unsigned long _timer, _fps_frames, _fps_timer;
6056  unsigned int _width, _height, _normalization;
6057  float _fps_fps, _min, _max;
6058  bool _is_fullscreen;
6059  char *_title;
6060  volatile unsigned int _window_width, _window_height, _button, _keys[128], _released_keys[128];
6061  volatile int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
6062  volatile bool _is_closed, _is_resized, _is_moved, _is_event,
6063  _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
6064  _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
6065  _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
6066  _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
6067  _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
6068  _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
6069  _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
6070  _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
6071  _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT, _is_keyARROWLEFT,
6072  _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3, _is_keyPAD4, _is_keyPAD5,
6073  _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB, _is_keyPADMUL, _is_keyPADDIV;
6074 
6076  //---------------------------
6077  //
6079 
6080  //---------------------------
6081 
6082 #ifdef cimgdisplay_plugin
6083 #include cimgdisplay_plugin
6084 #endif
6085 #ifdef cimgdisplay_plugin1
6086 #include cimgdisplay_plugin1
6087 #endif
6088 #ifdef cimgdisplay_plugin2
6089 #include cimgdisplay_plugin2
6090 #endif
6091 #ifdef cimgdisplay_plugin3
6092 #include cimgdisplay_plugin3
6093 #endif
6094 #ifdef cimgdisplay_plugin4
6095 #include cimgdisplay_plugin4
6096 #endif
6097 #ifdef cimgdisplay_plugin5
6098 #include cimgdisplay_plugin5
6099 #endif
6100 #ifdef cimgdisplay_plugin6
6101 #include cimgdisplay_plugin6
6102 #endif
6103 #ifdef cimgdisplay_plugin7
6104 #include cimgdisplay_plugin7
6105 #endif
6106 #ifdef cimgdisplay_plugin8
6107 #include cimgdisplay_plugin8
6108 #endif
6109 
6111  //--------------------------------------------------------
6112  //
6114 
6115  //--------------------------------------------------------
6116 
6118 
6122  assign();
6123  }
6124 
6126 
6137  _width(0),_height(0),_normalization(0),
6138  _min(0),_max(0),
6139  _is_fullscreen(false),
6140  _title(0),
6141  _window_width(0),_window_height(0),_button(0),
6142  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6143  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6144  assign();
6145  }
6146 
6148 
6156  CImgDisplay(const unsigned int width, const unsigned int height,
6157  const char *const title=0, const unsigned int normalization=3,
6158  const bool is_fullscreen=false, const bool is_closed=false):
6159  _width(0),_height(0),_normalization(0),
6160  _min(0),_max(0),
6161  _is_fullscreen(false),
6162  _title(0),
6163  _window_width(0),_window_height(0),_button(0),
6164  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6165  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6167  }
6168 
6170 
6177  template<typename T>
6178  explicit CImgDisplay(const CImg<T>& img,
6179  const char *const title=0, const unsigned int normalization=3,
6180  const bool is_fullscreen=false, const bool is_closed=false):
6181  _width(0),_height(0),_normalization(0),
6182  _min(0),_max(0),
6183  _is_fullscreen(false),
6184  _title(0),
6185  _window_width(0),_window_height(0),_button(0),
6186  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6187  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6189  }
6190 
6192 
6199  template<typename T>
6200  explicit CImgDisplay(const CImgList<T>& list,
6201  const char *const title=0, const unsigned int normalization=3,
6202  const bool is_fullscreen=false, const bool is_closed=false):
6203  _width(0),_height(0),_normalization(0),
6204  _min(0),_max(0),
6205  _is_fullscreen(false),
6206  _title(0),
6207  _window_width(0),_window_height(0),_button(0),
6208  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6209  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6211  }
6212 
6214 
6218  CImgDisplay(const CImgDisplay& disp):
6219  _width(0),_height(0),_normalization(0),
6220  _min(0),_max(0),
6221  _is_fullscreen(false),
6222  _title(0),
6223  _window_width(0),_window_height(0),_button(0),
6224  _window_x(0),_window_y(0),_mouse_x(-1),_mouse_y(-1),_wheel(0),
6225  _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
6226  assign(disp);
6227  }
6228 
6229 #if cimg_display==0
6230 
6231  static void _no_display_exception() {
6232  throw CImgDisplayException("CImgDisplay(): No display available.");
6233  }
6234 
6236 
6240  return flush();
6241  }
6242 
6244 
6246  CImgDisplay& assign(const unsigned int width, const unsigned int height,
6247  const char *const title=0, const unsigned int normalization=3,
6248  const bool is_fullscreen=false, const bool is_closed=false) {
6250  _no_display_exception();
6251  return assign();
6252  }
6253 
6255 
6257  template<typename T>
6259  const char *const title=0, const unsigned int normalization=3,
6260  const bool is_fullscreen=false, const bool is_closed=false) {
6261  _no_display_exception();
6262  return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
6263  }
6264 
6266 
6268  template<typename T>
6270  const char *const title=0, const unsigned int normalization=3,
6271  const bool is_fullscreen=false, const bool is_closed=false) {
6272  _no_display_exception();
6273  return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
6274  }
6275 
6277 
6280  _no_display_exception();
6281  return assign(disp._width,disp._height);
6282  }
6283 
6284 #endif
6285 
6287 
6295  static CImgDisplay& empty() {
6296  static CImgDisplay _empty;
6297  return _empty.assign();
6298  }
6299 
6300 #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,128,-85,false),CImgDisplay::_fitscreen(dx,dy,dz,128,-85,true)
6301  static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
6302  const int dmin, const int dmax,const bool return_y) {
6303  const unsigned int _nw = dx + (dz>1?dz:0), _nh = dy + (dz>1?dz:0);
6304  unsigned int nw = _nw?_nw:1, nh = _nh?_nh:1;
6305  const unsigned int
6307  mw = dmin<0?(unsigned int)(sw*-dmin/100):(unsigned int)dmin,
6308  mh = dmin<0?(unsigned int)(sh*-dmin/100):(unsigned int)dmin,
6309  Mw = dmax<0?(unsigned int)(sw*-dmax/100):(unsigned int)dmax,
6310  Mh = dmax<0?(unsigned int)(sh*-dmax/100):(unsigned int)dmax;
6311  if (nw<mw) { nh = nh*mw/nw; nh+=(nh==0?1:0); nw = mw; }
6312  if (nh<mh) { nw = nw*mh/nh; nw+=(nw==0?1:0); nh = mh; }
6313  if (nw>Mw) { nh = nh*Mw/nw; nh+=(nh==0?1:0); nw = Mw; }
6314  if (nh>Mh) { nw = nw*Mh/nh; nw+=(nw==0?1:0); nh = Mh; }
6315  if (nw<mw) nw = mw;
6316  if (nh<mh) nh = mh;
6317  return return_y?nh:nw;
6318  }
6319 
6321  //------------------------------------------
6322  //
6324 
6325  //------------------------------------------
6326 
6328 
6331  template<typename t>
6333  return display(img);
6334  }
6335 
6337 
6340  template<typename t>
6342  return display(list);
6343  }
6344 
6346 
6350  return assign(disp);
6351  }
6352 
6354 
6357  operator bool() const {
6358  return !is_empty();
6359  }
6360 
6362  //------------------------------------------
6363  //
6365 
6366  //------------------------------------------
6367 
6369 
6371  bool is_empty() const {
6372  return !(_width && _height);
6373  }
6374 
6376 
6381  bool is_closed() const {
6382  return _is_closed;
6383  }
6384 
6386 
6388  bool is_resized() const {
6389  return _is_resized;
6390  }
6391 
6393 
6395  bool is_moved() const {
6396  return _is_moved;
6397  }
6398 
6400 
6402  bool is_event() const {
6403  return _is_event;
6404  }
6405 
6407 
6409  bool is_fullscreen() const {
6410  return _is_fullscreen;
6411  }
6412 
6414 
6417  bool is_key() const {
6418  return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
6419  _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
6420  _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
6421  _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
6422  _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
6423  _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
6424  _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
6425  _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
6426  _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
6427  _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
6428  _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
6429  _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
6430  _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
6431  _is_keyK || _is_keyL || _is_keyENTER ||
6432  _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
6433  _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
6434  _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
6435  _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
6436  _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
6437  _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
6438  _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
6439  _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
6440  _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
6441  _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
6442  _is_keyPADMUL || _is_keyPADDIV;
6443  }
6444 
6446 
6459  bool is_key(const unsigned int keycode) const {
6460 #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
6461  _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
6462  _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
6463  _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
6464  _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
6465  _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
6466  _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
6467  _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
6468  _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
6469  _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
6470  _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
6471  _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
6472  _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
6473  _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
6474  _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
6475  _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
6476  _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
6477  _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
6478  _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
6479  _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
6480  _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
6481  _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
6482  _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
6483  _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
6484  _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
6485  _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
6486  return false;
6487  }
6488 
6490 
6503  bool is_key(const char *const keycode) const {
6504 #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
6505  _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
6506  _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
6507  _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
6508  _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
6509  _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
6510  _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
6511  _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
6512  _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
6513  _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
6514  _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
6515  _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
6516  _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
6517  _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
6518  _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
6519  _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
6520  _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
6521  _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
6522  _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
6523  _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
6524  _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
6525  _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
6526  _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
6527  _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
6528  _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
6529  _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
6530  return false;
6531  }
6532 
6534 
6550  bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length, const bool remove_sequence=false) {
6551  if (keycodes_sequence && length) {
6552  const unsigned int
6553  *const ps_end = keycodes_sequence + length - 1,
6554  *const pk_end = (unsigned int*)_keys + 1 + sizeof(_keys)/sizeof(unsigned int) - length,
6555  k = *ps_end;
6556  for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
6557  if (*(pk++)==k) {
6558  bool res = true;
6559  const unsigned int *ps = ps_end, *pk2 = pk;
6560  for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
6561  if (res) {
6562  if (remove_sequence) std::memset((void*)(pk-1),0,sizeof(unsigned int)*length);
6563  return true;
6564  }
6565  }
6566  }
6567  }
6568  return false;
6569  }
6570 
6571 #define _cimg_iskey_def(k) \
6572  bool is_key##k() const { \
6573  return _is_key##k; \
6574  }
6575 
6577 
6586  _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
6590  _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
6596  _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
6597  _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
6598  _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
6599  _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
6600  _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
6601  _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
6602  _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
6603  _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
6604  _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
6605 
6607  //------------------------------------------
6608  //
6610 
6611  //------------------------------------------
6612 
6613 #if cimg_display==0
6614 
6616 
6618  static int screen_width() {
6619  _no_display_exception();
6620  return 0;
6621  }
6622 
6624 
6626  static int screen_height() {
6627  _no_display_exception();
6628  return 0;
6629  }
6630 
6631 #endif
6632 
6634 
6638  int width() const {
6639  return (int)_width;
6640  }
6641 
6643 
6647  int height() const {
6648  return (int)_height;
6649  }
6650 
6652 
6671  unsigned int normalization() const {
6672  return _normalization;
6673  }
6674 
6676 
6679  const char *title() const {
6680  return _title;
6681  }
6682 
6684 
6688  int window_width() const {
6689  return (int)_window_width;
6690  }
6691 
6693 
6697  int window_height() const {
6698  return (int)_window_height;
6699  }
6700 
6702 
6705  int window_x() const {
6706  return _window_x;
6707  }
6708 
6710 
6713  int window_y() const {
6714  return _window_y;
6715  }
6716 
6718 
6723  int mouse_x() const {
6724  return _mouse_x;
6725  }
6726 
6728 
6733  int mouse_y() const {
6734  return _mouse_y;
6735  }
6736 
6738 
6762  unsigned int button() const {
6763  return _button;
6764  }
6765 
6767 
6789  int wheel() const {
6790  return _wheel;
6791  }
6792 
6794 
6806  unsigned int key(const unsigned int pos=0) const {
6807  return pos<(sizeof(_keys)/sizeof(unsigned int))?_keys[pos]:0;
6808  }
6809 
6811 
6823  unsigned int released_key(const unsigned int pos=0) const {
6824  return pos<(sizeof(_released_keys)/sizeof(unsigned int))?_released_keys[pos]:0;
6825  }
6826 
6828 
6836  static unsigned int keycode(const char *const keycode) {
6837 #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
6838  _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
6839  _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
6840  _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
6841  _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
6842  _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
6843  _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
6844  _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
6845  _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
6846  _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
6847  _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
6848  _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
6849  _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
6850  _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
6851  _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
6852  _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
6853  _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
6854  _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
6855  _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
6856  _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
6857  _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
6858  _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
6859  _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
6860  _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
6861  _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
6862  _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
6863  return 0;
6864  }
6865 
6867 
6872  if (!_fps_timer) _fps_timer = cimg::time();
6873  const float delta = (cimg::time()-_fps_timer)/1000.0f;
6874  ++_fps_frames;
6875  if (delta>=1) {
6876  _fps_fps = _fps_frames/delta;
6877  _fps_frames = 0;
6878  _fps_timer = cimg::time();
6879  }
6880  return _fps_fps;
6881  }
6882 
6884  //---------------------------------------
6885  //
6887 
6888  //---------------------------------------
6889 
6890 #if cimg_display==0
6891 
6893 
6897  template<typename T>
6898  CImgDisplay& display(const CImg<T>& img) {
6899  return assign(img);
6900  }
6901 
6902 #endif
6903 
6905 
6912  template<typename T>
6913  CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
6914  return display(list.get_append(axis,align));
6915  }
6916 
6917 #if cimg_display==0
6918 
6920 
6926  return assign();
6927  }
6928 
6930 
6937  return assign();
6938  }
6939 
6941 
6946  CImgDisplay& move(const int pos_x, const int pos_y) {
6947  return assign(pos_x,pos_y);
6948  }
6949 
6950 #endif
6951 
6953 
6959  CImgDisplay& resize(const bool force_redraw=true) {
6960  resize(_window_width,_window_height,force_redraw);
6961  return *this;
6962  }
6963 
6964 #if cimg_display==0
6965 
6967 
6973  CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
6974  return assign(width,height,0,3,force_redraw);
6975  }
6976 
6977 #endif
6978 
6980 
6987  template<typename T>
6988  CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
6989  return resize(img._width,img._height,force_redraw);
6990  }
6991 
6993 
7000  CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
7001  return resize(disp._width,disp._height,force_redraw);
7002  }
7003 
7004  // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
7005  template<typename t, typename T>
7006  static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
7007  t *ptrd, const unsigned int wd, const unsigned int hd) {
7008  unsigned int *const offx = new unsigned int[wd], *const offy = new unsigned int[hd+1], *poffx, *poffy;
7009  float s, curr, old;
7010  s = (float)ws/wd;
7011  poffx = offx; curr = 0; for (unsigned int x = 0; x<wd; ++x) { old = curr; curr+=s; *(poffx++) = (unsigned int)curr - (unsigned int)old; }
7012  s = (float)hs/hd;
7013  poffy = offy; curr = 0; for (unsigned int y = 0; y<hd; ++y) { old = curr; curr+=s; *(poffy++) = ws*((unsigned int)curr - (unsigned int)old); }
7014  *poffy = 0;
7015  poffy = offy;
7016  for (unsigned int y = 0; y<hd; ) {
7017  const T *ptr = ptrs;
7018  poffx = offx;
7019  for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poffx++); }
7020  ++y;
7021  unsigned int dy = *(poffy++);
7022  for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poffy++)) {}
7023  ptrs+=dy;
7024  }
7025  delete[] offx; delete[] offy;
7026  }
7027 
7029 
7033  _normalization = normalization;
7034  _min = _max = 0;
7035  return *this;
7036  }
7037 
7038 #if cimg_display==0
7039 
7041 
7053  CImgDisplay& set_title(const char *const format, ...) {
7054  return assign(0,0,format);
7055  }
7056 
7057 #endif
7058 
7060 
7070  CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
7071  if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
7072  return toggle_fullscreen(force_redraw);
7073  }
7074 
7075 #if cimg_display==0
7076 
7078 
7082  CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
7083  return assign(_width,_height,0,3,force_redraw);
7084  }
7085 
7087 
7091  return assign();
7092  }
7093 
7095 
7099  return assign();
7100  }
7101 
7103 
7106  CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
7107  return assign(pos_x,pos_y);
7108  }
7109 
7110 #endif
7111 
7113 
7117  _button = 0;
7118  _is_event = true;
7119  return *this;
7120  }
7121 
7123 
7127  CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
7128  const unsigned int buttoncode = button==1?1:button==2?2:button==3?4:0;
7129  if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
7130  _is_event = buttoncode?true:false;
7131  return *this;
7132  }
7133 
7135 
7139  _wheel = 0;
7140  _is_event = true;
7141  return *this;
7142  }
7143 
7145 
7149  CImgDisplay& set_wheel(const int amplitude) {
7150  _wheel+=amplitude;
7151  _is_event = amplitude?true:false;
7152  return *this;
7153  }
7154 
7156 
7160  std::memset((void*)_keys,0,sizeof(_keys));
7161  std::memset((void*)_released_keys,0,sizeof(_released_keys));
7162  _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 = _is_keyF9 =
7163  _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 = _is_key5 = _is_key6 =
7164  _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT = _is_keyHOME = _is_keyPAGEUP = _is_keyTAB =
7165  _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY = _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE =
7166  _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK = _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ =
7167  _is_keyK = _is_keyL = _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
7168  _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE = _is_keyALTGR = _is_keyAPPRIGHT =
7169  _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN = _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 =
7170  _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 = _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB =
7171  _is_keyPADMUL = _is_keyPADDIV = false;
7172  _is_event = true;
7173  return *this;
7174  }
7175 
7177 
7183  CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
7184 #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
7185  _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
7186  _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
7187  _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
7188  _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
7189  _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
7190  _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
7191  _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
7192  _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
7193  _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
7194  _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
7195  _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
7196  _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
7197  _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
7198  _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
7199  _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
7200  _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
7201  _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
7202  _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
7203  _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
7204  _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
7205  _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
7206  _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
7207  _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
7208  _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
7209  _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
7210  if (is_pressed) {
7211  if (*_keys)
7212  std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
7213  *_keys = keycode;
7214  if (*_released_keys) {
7215  std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
7216  *_released_keys = 0;
7217  }
7218  } else {
7219  if (*_keys) {
7220  std::memmove((void*)(_keys+1),(void*)_keys,sizeof(_keys) - sizeof(unsigned int));
7221  *_keys = 0;
7222  }
7223  if (*_released_keys)
7224  std::memmove((void*)(_released_keys+1),(void*)_released_keys,sizeof(_released_keys) - sizeof(unsigned int));
7225  *_released_keys = keycode;
7226  }
7227  _is_event = keycode?true:false;
7228  return *this;
7229  }
7230 
7232 
7237  _is_resized = _is_moved = _is_event = false;
7238  _fps_timer = _fps_frames = _timer = 0;
7239  _fps_fps = 0;
7240  return *this;
7241  }
7242 
7245  wait(*this);
7246  return *this;
7247  }
7248 
7250 
7254  CImgDisplay& wait(const unsigned int milliseconds) {
7255  cimg::_wait(milliseconds,_timer);
7256  return *this;
7257  }
7258 
7260  static void wait(CImgDisplay& disp1) {
7261  disp1._is_event = 0;
7262  while (!disp1._is_closed && !disp1._is_event) wait_all();
7263  }
7264 
7266  static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
7267  disp1._is_event = disp2._is_event = 0;
7268  while ((!disp1._is_closed || !disp2._is_closed) &&
7269  !disp1._is_event && !disp2._is_event) wait_all();
7270  }
7271 
7273  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
7274  disp1._is_event = disp2._is_event = disp3._is_event = 0;
7275  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
7276  !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
7277  }
7278 
7280  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
7281  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = 0;
7282  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
7283  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
7284  }
7285 
7287  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5) {
7288  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = 0;
7289  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
7290  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event) wait_all();
7291  }
7292 
7294  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7295  CImgDisplay& disp6) {
7296  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7297  disp6._is_event = 0;
7298  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7299  !disp6._is_closed) &&
7300  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7301  !disp6._is_event) wait_all();
7302  }
7303 
7305  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7306  CImgDisplay& disp6, CImgDisplay& disp7) {
7307  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7308  disp6._is_event = disp7._is_event = 0;
7309  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7310  !disp6._is_closed || !disp7._is_closed) &&
7311  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7312  !disp6._is_event && !disp7._is_event) wait_all();
7313  }
7314 
7316  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7317  CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
7318  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7319  disp6._is_event = disp7._is_event = disp8._is_event = 0;
7320  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7321  !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
7322  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7323  !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
7324  }
7325 
7327  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7328  CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
7329  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7330  disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = 0;
7331  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7332  !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
7333  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7334  !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
7335  }
7336 
7338  static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
7339  CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9, CImgDisplay& disp10) {
7340  disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
7341  disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = 0;
7342  while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
7343  !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
7344  !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
7345  !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event) wait_all();
7346  }
7347 
7348 #if cimg_display==0
7349 
7351  static void wait_all() {
7352  return _no_display_exception();
7353  }
7354 
7356 
7363  template<typename T>
7364  CImgDisplay& render(const CImg<T>& img) {
7365  return assign(img);
7366  }
7367 
7369 
7375  return assign();
7376  }
7377 
7379 
7382  template<typename T>
7383  const CImgDisplay& snapshot(CImg<T>& img) const {
7384  cimg::unused(img);
7385  _no_display_exception();
7386  return *this;
7387  }
7388 #endif
7389 
7390  // X11-based implementation
7391  //--------------------------
7392 #if cimg_display==1
7393 
7394  Atom _wm_window_atom, _wm_protocol_atom;
7395  Window _window, _background_window;
7396  Colormap _colormap;
7397  XImage *_image;
7398  void *_data;
7399 #ifdef cimg_use_xshm
7400  XShmSegmentInfo *_shminfo;
7401 #endif
7402 
7403  static int screen_width() {
7404  Display *const dpy = cimg::X11_attr().display;
7405  int res = 0;
7406  if (!dpy) {
7407  Display *const _dpy = XOpenDisplay(0);
7408  if (!_dpy)
7409  throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
7410  res = DisplayWidth(_dpy,DefaultScreen(_dpy));
7411  XCloseDisplay(_dpy);
7412  } else {
7413 #ifdef cimg_use_xrandr
7414  if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
7415  res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
7416  else res = DisplayWidth(dpy,DefaultScreen(dpy));
7417 #else
7418  res = DisplayWidth(dpy,DefaultScreen(dpy));
7419 #endif
7420  }
7421  return res;
7422  }
7423 
7424  static int screen_height() {
7425  Display *const dpy = cimg::X11_attr().display;
7426  int res = 0;
7427  if (!dpy) {
7428  Display *const _dpy = XOpenDisplay(0);
7429  if (!_dpy)
7430  throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
7431  res = DisplayHeight(_dpy,DefaultScreen(_dpy));
7432  XCloseDisplay(_dpy);
7433  } else {
7434 #ifdef cimg_use_xrandr
7435  if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
7436  res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
7437  else res = DisplayHeight(dpy,DefaultScreen(dpy));
7438 #else
7439  res = DisplayHeight(dpy,DefaultScreen(dpy));
7440 #endif
7441  }
7442  return res;
7443  }
7444 
7445  static void wait_all() {
7446  Display *const dpy = cimg::X11_attr().display;
7447  if (!dpy) return;
7448  XLockDisplay(dpy);
7449  bool flag = true;
7450  XEvent event;
7451  while (flag) {
7452  XNextEvent(dpy,&event);
7453  for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
7454  if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window) {
7455  cimg::X11_attr().wins[i]->_handle_events(&event);
7456  if (cimg::X11_attr().wins[i]->_is_event) flag = false;
7457  }
7458  }
7459  XUnlockDisplay(dpy);
7460  }
7461 
7462  void _handle_events(const XEvent *const pevent) {
7463  Display *const dpy = cimg::X11_attr().display;
7464  XEvent event = *pevent;
7465  switch (event.type) {
7466  case ClientMessage : {
7467  if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
7468  (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
7469  XUnmapWindow(cimg::X11_attr().display,_window);
7470  _is_closed = _is_event = true;
7471  }
7472  } break;
7473  case ConfigureNotify : {
7474  while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
7475  const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
7476  const int nx = event.xconfigure.x, ny = event.xconfigure.y;
7477  if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
7478  _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
7479  XResizeWindow(dpy,_window,_window_width,_window_height);
7480  _is_resized = _is_event = true;
7481  }
7482  if (nx!=_window_x || ny!=_window_y) { _window_x = nx; _window_y = ny; _is_moved = _is_event = true; }
7483  } break;
7484  case Expose : {
7485  while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
7486  _paint(false);
7487  if (_is_fullscreen) {
7488  XWindowAttributes attr;
7489  XGetWindowAttributes(dpy,_window,&attr);
7490  while (attr.map_state!=IsViewable) XSync(dpy,0);
7491  XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
7492  }
7493  } break;
7494  case ButtonPress : {
7495  do {
7496  _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
7497  if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7498  switch (event.xbutton.button) {
7499  case 1 : set_button(1); break;
7500  case 3 : set_button(2); break;
7501  case 2 : set_button(3); break;
7502  }
7503  } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
7504  } break;
7505  case ButtonRelease : {
7506  do {
7507  _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
7508  if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7509  switch (event.xbutton.button) {
7510  case 1 : set_button(1,false); break;
7511  case 3 : set_button(2,false); break;
7512  case 2 : set_button(3,false); break;
7513  case 4 : set_wheel(1); break;
7514  case 5 : set_wheel(-1); break;
7515  }
7516  } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
7517  } break;
7518  case KeyPress : {
7519  char tmp = 0; KeySym ksym;
7520  XLookupString(&event.xkey,&tmp,1,&ksym,0);
7521  set_key((unsigned int)ksym,true);
7522  } break;
7523  case KeyRelease : {
7524  char keys_return[32]; // Check that the key has been physically unpressed.
7525  XQueryKeymap(dpy,keys_return);
7526  const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
7527  const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
7528  if (!is_key_pressed) {
7529  char tmp = 0; KeySym ksym;
7530  XLookupString(&event.xkey,&tmp,1,&ksym,0);
7531  set_key((unsigned int)ksym,false);
7532  }
7533  } break;
7534  case EnterNotify: {
7535  while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
7536  _mouse_x = event.xmotion.x;
7537  _mouse_y = event.xmotion.y;
7538  if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7539  } break;
7540  case LeaveNotify : {
7541  while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
7542  _mouse_x = _mouse_y =-1; _is_event = true;
7543  } break;
7544  case MotionNotify : {
7545  while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
7546  _mouse_x = event.xmotion.x;
7547  _mouse_y = event.xmotion.y;
7548  if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
7549  _is_event = true;
7550  } break;
7551  }
7552  }
7553 
7554  static void* _events_thread(void *) { // Only one thread to handle events for all opened display windows.
7555  Display *const dpy = cimg::X11_attr().display;
7556  XEvent event;
7557  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
7558  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
7559  for (;;) {
7560  XLockDisplay(dpy);
7561  bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
7562  if (!event_flag) event_flag = XCheckMaskEvent(dpy,
7563  ExposureMask | StructureNotifyMask | ButtonPressMask|
7564  KeyPressMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask|
7565  ButtonReleaseMask | KeyReleaseMask,&event);
7566  if (event_flag)
7567  for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
7568  if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
7569  cimg::X11_attr().wins[i]->_handle_events(&event);
7570  XUnlockDisplay(dpy);
7571  pthread_testcancel();
7572  cimg::sleep(8);
7573  }
7574  return 0;
7575  }
7576 
7577  void _set_colormap(Colormap& _colormap, const unsigned int dim) {
7578  XColor colormap[256];
7579  switch (dim) {
7580  case 1 : { // colormap for greyscale images
7581  for (unsigned int index = 0; index<256; ++index) {
7582  colormap[index].pixel = index;
7583  colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
7584  colormap[index].flags = DoRed | DoGreen | DoBlue;
7585  }
7586  } break;
7587  case 2 : { // colormap for RG images
7588  for (unsigned int index = 0, r = 8; r<256; r+=16)
7589  for (unsigned int g = 8; g<256; g+=16) {
7590  colormap[index].pixel = index;
7591  colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
7592  colormap[index].green = (unsigned short)(g<<8);
7593  colormap[index++].flags = DoRed | DoGreen | DoBlue;
7594  }
7595  } break;
7596  default : { // colormap for RGB images
7597  for (unsigned int index = 0, r = 16; r<256; r+=32)
7598  for (unsigned int g = 16; g<256; g+=32)
7599  for (unsigned int b = 32; b<256; b+=64) {
7600  colormap[index].pixel = index;
7601  colormap[index].red = (unsigned short)(r<<8);
7602  colormap[index].green = (unsigned short)(g<<8);
7603  colormap[index].blue = (unsigned short)(b<<8);
7604  colormap[index++].flags = DoRed | DoGreen | DoBlue;
7605  }
7606  }
7607  }
7608  XStoreColors(cimg::X11_attr().display,_colormap,colormap,256);
7609  }
7610 
7611  void _map_window() {
7612  Display *const dpy = cimg::X11_attr().display;
7613  bool is_exposed = false, is_mapped = false;
7614  XWindowAttributes attr;
7615  XEvent event;
7616  XMapRaised(dpy,_window);
7617  do { // Wait for the window to be mapped.
7618  XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
7619  switch (event.type) {
7620  case MapNotify : is_mapped = true; break;
7621  case Expose : is_exposed = true; break;
7622  }
7623  } while (!is_exposed || !is_mapped);
7624  do { // Wait for the window to be visible.
7625  XGetWindowAttributes(dpy,_window,&attr);
7626  if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
7627  } while (attr.map_state!=IsViewable);
7628  _window_x = attr.x;
7629  _window_y = attr.y;
7630  }
7631 
7632  void _paint(const bool wait_expose=true) {
7633  if (_is_closed || !_image) return;
7634  Display *const dpy = cimg::X11_attr().display;
7635  if (wait_expose) { // Send an expose event sticked to display window to force repaint.
7636  static XEvent event;
7637  event.xexpose.type = Expose;
7638  event.xexpose.serial = 0;
7639  event.xexpose.send_event = 1;
7640  event.xexpose.display = dpy;
7641  event.xexpose.window = _window;
7642  event.xexpose.x = 0;
7643  event.xexpose.y = 0;
7644  event.xexpose.width = width();
7645  event.xexpose.height = height();
7646  event.xexpose.count = 0;
7647  XSendEvent(dpy,_window,0,0,&event);
7648  } else { // Repaint directly (may be called from the expose event).
7649  GC gc = DefaultGC(dpy,DefaultScreen(dpy));
7650 #ifdef cimg_use_xshm
7651  if (_shminfo) {
7652  const int completion_type = XShmGetEventBase(dpy) + ShmCompletion;
7653  XEvent event;
7654  XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
7655  do { XNextEvent(dpy,&event); } while (event.type!=completion_type); // Wait for the image drawing to be completed.
7656  } else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
7657 #else
7658  XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
7659 #endif
7660  }
7661  }
7662 
7663  template<typename T>
7664  void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
7665  Display *const dpy = cimg::X11_attr().display;
7666  cimg::unused(pixel_type);
7667 
7668 #ifdef cimg_use_xshm
7669  if (_shminfo) {
7670  XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
7671  XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
7672  cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
7673  if (!nimage) { delete nshminfo; return; }
7674  else {
7675  nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
7676  if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
7677  else {
7678  nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
7679  if (nshminfo->shmaddr==(char*)-1) { shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return; }
7680  else {
7681  nshminfo->readOnly = 0;
7682  cimg::X11_attr().is_shm_enabled = true;
7683  XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
7684  XShmAttach(dpy,nshminfo);
7685  XFlush(dpy);
7686  XSetErrorHandler(oldXErrorHandler);
7687  if (!cimg::X11_attr().is_shm_enabled) {
7688  shmdt(nshminfo->shmaddr);
7689  shmctl(nshminfo->shmid,IPC_RMID,0);
7690  XDestroyImage(nimage);
7691  delete nshminfo;
7692  return;
7693  } else {
7694  T *const ndata = (T*)nimage->data;
7695  if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
7696  else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
7697  XShmDetach(dpy,_shminfo);
7698  XDestroyImage(_image);
7699  shmdt(_shminfo->shmaddr);
7700  shmctl(_shminfo->shmid,IPC_RMID,0);
7701  delete _shminfo;
7702  _shminfo = nshminfo;
7703  _image = nimage;
7704  _data = (void*)ndata;
7705  }
7706  }
7707  }
7708  }
7709  } else
7710 #endif
7711  {
7712  T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
7713  if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
7714  else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
7715  _data = (void*)ndata;
7716  XDestroyImage(_image);
7717  _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
7718  cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
7719  }
7720  }
7721 
7722  void _init_fullscreen() {
7723  if (!_is_fullscreen || _is_closed) return;
7724  Display *const dpy = cimg::X11_attr().display;
7725  _background_window = 0;
7726 
7727 #ifdef cimg_use_xrandr
7728  int foo;
7729  if (XRRQueryExtension(dpy,&foo,&foo)) {
7730  XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
7731  if (!cimg::X11_attr().resolutions) {
7732  cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
7733  cimg::X11_attr().nb_resolutions = (unsigned int)foo;
7734  }
7735  if (cimg::X11_attr().resolutions) {
7736  cimg::X11_attr().curr_resolution = 0;
7737  for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
7738  const unsigned int
7739  nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
7740  nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
7741  if (nw>=_width && nh>=_height &&
7742  nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
7743  nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
7744  cimg::X11_attr().curr_resolution = i;
7745  }
7746  if (cimg::X11_attr().curr_resolution>0) {
7747  XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
7748  XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
7749  cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
7750  XRRFreeScreenConfigInfo(config);
7751  XSync(dpy,0);
7752  }
7753  }
7754  }
7755  if (!cimg::X11_attr().resolutions)
7756  cimg::warn(_cimgdisplay_instance
7757  "init_fullscreen(): Xrandr extension not supported by the X server.",
7758  cimgdisplay_instance);
7759 #endif
7760 
7761  const unsigned int sx = screen_width(), sy = screen_height();
7762  if (sx==_width && sy==_height) return;
7763  XSetWindowAttributes winattr;
7764  winattr.override_redirect = 1;
7765  _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
7766  InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
7767  const unsigned long buf_size = (unsigned long)sx*sy*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
7768  void *background_data = std::malloc(buf_size);
7769  std::memset(background_data,0,buf_size);
7770  XImage *background_image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
7771  ZPixmap,0,(char*)background_data,sx,sy,8,0);
7772  XEvent event;
7773  XSelectInput(dpy,_background_window,StructureNotifyMask);
7774  XMapRaised(dpy,_background_window);
7775  do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
7776  while (event.type!=MapNotify);
7777  GC gc = DefaultGC(dpy,DefaultScreen(dpy));
7778 #ifdef cimg_use_xshm
7779  if (_shminfo) XShmPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy,0);
7780  else XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
7781 #else
7782  XPutImage(dpy,_background_window,gc,background_image,0,0,0,0,sx,sy);
7783 #endif
7784  XWindowAttributes attr;
7785  XGetWindowAttributes(dpy,_background_window,&attr);
7786  while (attr.map_state!=IsViewable) XSync(dpy,0);
7787  XDestroyImage(background_image);
7788  }
7789 
7790  void _desinit_fullscreen() {
7791  if (!_is_fullscreen) return;
7792  Display *const dpy = cimg::X11_attr().display;
7793  XUngrabKeyboard(dpy,CurrentTime);
7794 #ifdef cimg_use_xrandr
7795  if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
7796  XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
7797  XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
7798  XRRFreeScreenConfigInfo(config);
7799  XSync(dpy,0);
7800  cimg::X11_attr().curr_resolution = 0;
7801  }
7802 #endif
7803  if (_background_window) XDestroyWindow(dpy,_background_window);
7804  _background_window = 0;
7805  _is_fullscreen = false;
7806  }
7807 
7808  static int _assign_xshm(Display *dpy, XErrorEvent *error) {
7809  cimg::unused(dpy,error);
7810  cimg::X11_attr().is_shm_enabled = false;
7811  return 0;
7812  }
7813 
7814  void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
7815  const unsigned int normalization_type=3,
7816  const bool fullscreen_flag=false, const bool closed_flag=false) {
7817 
7818  // Allocate space for window title
7819  const char *const nptitle = ptitle?ptitle:"";
7820  const unsigned int s = std::strlen(nptitle) + 1;
7821  char *const tmp_title = s?new char[s]:0;
7822  if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
7823 
7824  // Destroy previous display window if existing
7825  if (!is_empty()) assign();
7826 
7827  // Open X11 display and retrieve graphical properties.
7828  Display* &dpy = cimg::X11_attr().display;
7829  if (!dpy) {
7830  static const int xinit_status = XInitThreads();
7831  cimg::unused(xinit_status);
7832  dpy = XOpenDisplay(0);
7833  if (!dpy)
7834  throw CImgDisplayException(_cimgdisplay_instance
7835  "assign(): Failed to open X11 display.",
7836  cimgdisplay_instance);
7837 
7838  cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
7839  if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 && cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
7840  throw CImgDisplayException(_cimgdisplay_instance
7841  "assign(): Invalid %u bits screen mode detected "
7842  "(only 8, 16, 24 and 32 bits modes are managed).",
7843  cimgdisplay_instance,
7844  cimg::X11_attr().nb_bits);
7845  XVisualInfo vtemplate;
7846  vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
7847  int nb_visuals;
7848  XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
7849  if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
7850  cimg::X11_attr().byte_order = ImageByteOrder(dpy);
7851  XFree(vinfo);
7852 
7853  XLockDisplay(dpy);
7854  cimg::X11_attr().event_thread = new pthread_t;
7855  pthread_create(cimg::X11_attr().event_thread,0,_events_thread,0);
7856  } else XLockDisplay(dpy);
7857 
7858  // Set display variables.
7859  _width = cimg::min(dimw,(unsigned int)screen_width());
7860  _height = cimg::min(dimh,(unsigned int)screen_height());
7861  _normalization = normalization_type<4?normalization_type:3;
7862  _is_fullscreen = fullscreen_flag;
7863  _window_x = _window_y = 0;
7864  _is_closed = closed_flag;
7865  _title = tmp_title;
7866  flush();
7867 
7868  // Create X11 window (and LUT, if 8bits display)
7869  if (_is_fullscreen) {
7870  if (!_is_closed) _init_fullscreen();
7871  const unsigned int sx = screen_width(), sy = screen_height();
7872  XSetWindowAttributes winattr;
7873  winattr.override_redirect = 1;
7874  _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx-_width)/2,(sy-_height)/2,_width,_height,0,0,
7875  InputOutput,CopyFromParent,CWOverrideRedirect,&winattr);
7876  } else
7877  _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
7878 
7879  XSelectInput(dpy,_window,
7880  ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
7881  EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
7882 
7883  XStoreName(dpy,_window,_title?_title:" ");
7884  if (cimg::X11_attr().nb_bits==8) {
7885  _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
7886  _set_colormap(_colormap,3);
7887  XSetWindowColormap(dpy,_window,_colormap);
7888  }
7889 
7890  static const char *const _window_class = cimg_appname;
7891  XClassHint *const window_class = XAllocClassHint();
7892  window_class->res_name = (char*)_window_class;
7893  window_class->res_class = (char*)_window_class;
7894  XSetClassHint(dpy,_window,window_class);
7895  XFree(window_class);
7896 
7897  _window_width = _width;
7898  _window_height = _height;
7899 
7900  // Create XImage
7901 #ifdef cimg_use_xshm
7902  _shminfo = 0;
7903  if (XShmQueryExtension(dpy)) {
7904  _shminfo = new XShmSegmentInfo;
7905  _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,ZPixmap,0,_shminfo,_width,_height);
7906  if (!_image) { delete _shminfo; _shminfo = 0; }
7907  else {
7908  _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
7909  if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
7910  else {
7911  _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
7912  if (_shminfo->shmaddr==(char*)-1) { shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
7913  else {
7914  _shminfo->readOnly = 0;
7915  cimg::X11_attr().is_shm_enabled = true;
7916  XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
7917  XShmAttach(dpy,_shminfo);
7918  XSync(dpy,0);
7919  XSetErrorHandler(oldXErrorHandler);
7920  if (!cimg::X11_attr().is_shm_enabled) {
7921  shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
7922  }
7923  }
7924  }
7925  }
7926  }
7927  if (!_shminfo)
7928 #endif
7929  {
7930  const unsigned long buf_size = (unsigned long)_width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
7931  _data = std::malloc(buf_size);
7932  _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,_width,_height,8,0);
7933  }
7934 
7935  _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
7936  _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
7937  XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
7938 
7939  if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
7940  cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
7941  if (!_is_closed) _map_window(); else { _window_x = _window_y = cimg::type<int>::min(); }
7942  XUnlockDisplay(dpy);
7943  }
7944 
7945  CImgDisplay& assign() {
7946  if (is_empty()) return flush();
7947  Display *const dpy = cimg::X11_attr().display;
7948  XLockDisplay(dpy);
7949 
7950  // Remove display window from event thread list.
7951  unsigned int i;
7952  for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
7953  for (; i<cimg::X11_attr().nb_wins-1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i+1];
7954  --cimg::X11_attr().nb_wins;
7955 
7956  // Destroy window, image, colormap and title.
7957  if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
7958  XDestroyWindow(dpy,_window);
7959  _window = 0;
7960 #ifdef cimg_use_xshm
7961  if (_shminfo) {
7962  XShmDetach(dpy,_shminfo);
7963  XDestroyImage(_image);
7964  shmdt(_shminfo->shmaddr);
7965  shmctl(_shminfo->shmid,IPC_RMID,0);
7966  delete _shminfo;
7967  _shminfo = 0;
7968  } else
7969 #endif
7970  XDestroyImage(_image);
7971  _data = 0; _image = 0;
7972  if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
7973  _colormap = 0;
7974  XSync(dpy,0);
7975 
7976  // Reset display variables
7977  delete[] _title;
7978  _width = _height = _normalization = _window_width = _window_height = 0;
7979  _window_x = _window_y = 0;
7980  _is_fullscreen = false;
7981  _is_closed = true;
7982  _min = _max = 0;
7983  _title = 0;
7984  flush();
7985 
7986  // End event thread and close display if necessary
7987  XUnlockDisplay(dpy);
7988  if (!cimg::X11_attr().nb_wins) {
7989  // Kill event thread
7990  //pthread_cancel(*cimg::X11_attr().event_thread);
7991  //XUnlockDisplay(cimg::X11_attr().display);
7992  //pthread_join(*cimg::X11_attr().event_thread,0);
7993  //delete cimg::X11_attr().event_thread;
7994  //cimg::X11_attr().event_thread = 0;
7995  // XUnlockDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required).
7996  // XCloseDisplay(cimg::X11_attr().display); // <- This call make the library hang sometimes (fix required).
7997  //cimg::X11_attr().display = 0;
7998  }
7999  return *this;
8000  }
8001 
8002  CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
8003  const unsigned int normalization_type=3,
8004  const bool fullscreen_flag=false, const bool closed_flag=false) {
8005  if (!dimw || !dimh) return assign();
8006  _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
8007  _min = _max = 0;
8008  std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
8009  (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*(unsigned long)_width*_height);
8010  return paint();
8011  }
8012 
8013  template<typename T>
8014  CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
8015  const unsigned int normalization_type=3,
8016  const bool fullscreen_flag=false, const bool closed_flag=false) {
8017  if (!img) return assign();
8018  CImg<T> tmp;
8019  const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8020  _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8021  if (_normalization==2) _min = (float)nimg.min_max(_max);
8022  return render(nimg).paint();
8023  }
8024 
8025  template<typename T>
8026  CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
8027  const unsigned int normalization_type=3,
8028  const bool fullscreen_flag=false, const bool closed_flag=false) {
8029  if (!list) return assign();
8030  CImg<T> tmp;
8031  const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8032  _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8033  if (_normalization==2) _min = (float)nimg.min_max(_max);
8034  return render(nimg).paint();
8035  }
8036 
8037  CImgDisplay& assign(const CImgDisplay& disp) {
8038  if (!disp) return assign();
8039  _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
8040  std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
8041  cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
8042  sizeof(unsigned int))*(unsigned long)_width*_height);
8043  return paint();
8044  }
8045 
8046  CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
8047  if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
8048  if (is_empty()) return assign(nwidth,nheight);
8049  Display *const dpy = cimg::X11_attr().display;
8050  const unsigned int
8051  tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
8052  tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
8053  dimx = tmpdimx?tmpdimx:1,
8054  dimy = tmpdimy?tmpdimy:1;
8055  XLockDisplay(dpy);
8056  if (_window_width!=dimx || _window_height!=dimy) XResizeWindow(dpy,_window,dimx,dimy);
8057  if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
8058  case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
8059  case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
8060  default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
8061  }
8062  _window_width = _width = dimx; _window_height = _height = dimy;
8063  _is_resized = false;
8064  XUnlockDisplay(dpy);
8065  if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
8066  if (force_redraw) return paint();
8067  return *this;
8068  }
8069 
8070  CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
8071  if (is_empty()) return *this;
8072  if (force_redraw) {
8073  const unsigned long buf_size = (unsigned long)_width*_height*(cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
8074  void *image_data = std::malloc(buf_size);
8075  std::memcpy(image_data,_data,buf_size);
8076  assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8077  std::memcpy(_data,image_data,buf_size);
8078  std::free(image_data);
8079  return paint();
8080  }
8081  return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8082  }
8083 
8084  CImgDisplay& show() {
8085  if (is_empty() || !_is_closed) return *this;
8086  Display *const dpy = cimg::X11_attr().display;
8087  XLockDisplay(dpy);
8088  if (_is_fullscreen) _init_fullscreen();
8089  _map_window();
8090  _is_closed = false;
8091  XUnlockDisplay(dpy);
8092  return paint();
8093  }
8094 
8095  CImgDisplay& close() {
8096  if (is_empty() || _is_closed) return *this;
8097  Display *const dpy = cimg::X11_attr().display;
8098  XLockDisplay(dpy);
8099  if (_is_fullscreen) _desinit_fullscreen();
8100  XUnmapWindow(dpy,_window);
8101  _window_x = _window_y = -1;
8102  _is_closed = true;
8103  XUnlockDisplay(dpy);
8104  return *this;
8105  }
8106 
8107  CImgDisplay& move(const int posx, const int posy) {
8108  if (is_empty()) return *this;
8109  Display *const dpy = cimg::X11_attr().display;
8110  show();
8111  XLockDisplay(dpy);
8112  XMoveWindow(dpy,_window,posx,posy);
8113  _window_x = posx; _window_y = posy;
8114  _is_moved = false;
8115  XUnlockDisplay(dpy);
8116  return paint();
8117  }
8118 
8119  CImgDisplay& show_mouse() {
8120  if (is_empty()) return *this;
8121  Display *const dpy = cimg::X11_attr().display;
8122  XLockDisplay(dpy);
8123  XUndefineCursor(dpy,_window);
8124  XUnlockDisplay(dpy);
8125  return *this;
8126  }
8127 
8128  CImgDisplay& hide_mouse() {
8129  if (is_empty()) return *this;
8130  Display *const dpy = cimg::X11_attr().display;
8131  XLockDisplay(dpy);
8132  const char pix_data[8] = { 0 };
8133  XColor col;
8134  col.red = col.green = col.blue = 0;
8135  Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
8136  Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
8137  XFreePixmap(dpy,pix);
8138  XDefineCursor(dpy,_window,cur);
8139  XUnlockDisplay(dpy);
8140  return *this;
8141  }
8142 
8143  CImgDisplay& set_mouse(const int posx, const int posy) {
8144  if (is_empty() || _is_closed) return *this;
8145  Display *const dpy = cimg::X11_attr().display;
8146  XLockDisplay(dpy);
8147  XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
8148  _mouse_x = posx; _mouse_y = posy;
8149  _is_moved = false;
8150  XSync(dpy,0);
8151  XUnlockDisplay(dpy);
8152  return *this;
8153  }
8154 
8155  CImgDisplay& set_title(const char *const format, ...) {
8156  if (is_empty()) return *this;
8157  char tmp[1024] = { 0 };
8158  va_list ap;
8159  va_start(ap, format);
8160  cimg_vsnprintf(tmp,sizeof(tmp),format,ap);
8161  va_end(ap);
8162  if (!std::strcmp(_title,tmp)) return *this;
8163  delete[] _title;
8164  const unsigned int s = std::strlen(tmp) + 1;
8165  _title = new char[s];
8166  std::memcpy(_title,tmp,s*sizeof(char));
8167  Display *const dpy = cimg::X11_attr().display;
8168  XLockDisplay(dpy);
8169  XStoreName(dpy,_window,tmp);
8170  XUnlockDisplay(dpy);
8171  return *this;
8172  }
8173 
8174  template<typename T>
8175  CImgDisplay& display(const CImg<T>& img) {
8176  if (!img)
8177  throw CImgArgumentException(_cimgdisplay_instance
8178  "display(): Empty specified image.",
8179  cimgdisplay_instance);
8180  if (is_empty()) return assign(img);
8181  return render(img).paint(false);
8182  }
8183 
8184  CImgDisplay& paint(const bool wait_expose=true) {
8185  if (is_empty()) return *this;
8186  Display *const dpy = cimg::X11_attr().display;
8187  XLockDisplay(dpy);
8188  _paint(wait_expose);
8189  XUnlockDisplay(dpy);
8190  return *this;
8191  }
8192 
8193  template<typename T>
8194  CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
8195  if (!img)
8196  throw CImgArgumentException(_cimgdisplay_instance
8197  "render(): Empty specified image.",
8198  cimgdisplay_instance);
8199  if (is_empty()) return *this;
8200  if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8201  if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height)) return render(img.get_resize(_width,_height,1,-100,1));
8202  if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
8203  static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
8204  return render(img.get_index(default_colormap,1,false));
8205  }
8206 
8207  Display *const dpy = cimg::X11_attr().display;
8208  const T
8209  *data1 = img._data,
8210  *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
8211  *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
8212 
8213  if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
8214  XLockDisplay(dpy);
8215 
8216  if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
8217  _min = _max = 0;
8218  switch (cimg::X11_attr().nb_bits) {
8219  case 8 : { // 256 colormap, no normalization
8220  _set_colormap(_colormap,img._spectrum);
8221  unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[(unsigned long)img._width*img._height];
8222  unsigned char *ptrd = (unsigned char*)ndata;
8223  switch (img._spectrum) {
8224  case 1 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) (*ptrd++) = (unsigned char)*(data1++);
8225  break;
8226  case 2 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8227  const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++);
8228  (*ptrd++) = (R&0xf0) | (G>>4);
8229  } break;
8230  default : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8231  const unsigned char R = (unsigned char)*(data1++), G = (unsigned char)*(data2++), B = (unsigned char)*(data3++);
8232  (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
8233  }
8234  }
8235  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
8236  } break;
8237  case 16 : { // 16 bits colors, no normalization
8238  unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[(unsigned long)img._width*img._height];
8239  unsigned char *ptrd = (unsigned char*)ndata;
8240  const unsigned int M = 248;
8241  switch (img._spectrum) {
8242  case 1 :
8243  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8244  const unsigned char val = (unsigned char)*(data1++), G = val>>2;
8245  *(ptrd++) = (val&M) | (G>>3);
8246  *(ptrd++) = (G<<5) | (G>>1);
8247  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8248  const unsigned char val = (unsigned char)*(data1++), G = val>>2;
8249  *(ptrd++) = (G<<5) | (G>>1);
8250  *(ptrd++) = (val&M) | (G>>3);
8251  }
8252  break;
8253  case 2 :
8254  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8255  const unsigned char G = (unsigned char)*(data2++)>>2;
8256  *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8257  *(ptrd++) = (G<<5);
8258  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8259  const unsigned char G = (unsigned char)*(data2++)>>2;
8260  *(ptrd++) = (G<<5);
8261  *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8262  }
8263  break;
8264  default :
8265  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8266  const unsigned char G = (unsigned char)*(data2++)>>2;
8267  *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8268  *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
8269  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8270  const unsigned char G = (unsigned char)*(data2++)>>2;
8271  *(ptrd++) = (G<<5) | ((unsigned char)*(data3++)>>3);
8272  *(ptrd++) = ((unsigned char)*(data1++)&M) | (G>>3);
8273  }
8274  }
8275  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
8276  } break;
8277  default : { // 24 bits colors, no normalization
8278  unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[(unsigned long)img._width*img._height];
8279  if (sizeof(int)==4) { // 32 bits int uses optimized version
8280  unsigned int *ptrd = ndata;
8281  switch (img._spectrum) {
8282  case 1 :
8283  if (cimg::X11_attr().byte_order==cimg::endianness())
8284  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8285  const unsigned char val = (unsigned char)*(data1++);
8286  *(ptrd++) = (val<<16) | (val<<8) | val;
8287  }
8288  else
8289  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8290  const unsigned char val = (unsigned char)*(data1++);
8291  *(ptrd++) = (val<<16) | (val<<8) | val;
8292  }
8293  break;
8294  case 2 :
8295  if (cimg::X11_attr().byte_order==cimg::endianness())
8296  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8297  *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
8298  else
8299  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8300  *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
8301  break;
8302  default :
8303  if (cimg::X11_attr().byte_order==cimg::endianness())
8304  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8305  *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
8306  else
8307  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8308  *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
8309  }
8310  } else {
8311  unsigned char *ptrd = (unsigned char*)ndata;
8312  switch (img._spectrum) {
8313  case 1 :
8314  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8315  *(ptrd++) = 0;
8316  *(ptrd++) = (unsigned char)*(data1++);
8317  *(ptrd++) = 0;
8318  *(ptrd++) = 0;
8319  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8320  *(ptrd++) = 0;
8321  *(ptrd++) = 0;
8322  *(ptrd++) = (unsigned char)*(data1++);
8323  *(ptrd++) = 0;
8324  }
8325  break;
8326  case 2 :
8327  if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
8328  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8329  *(ptrd++) = 0;
8330  *(ptrd++) = (unsigned char)*(data2++);
8331  *(ptrd++) = (unsigned char)*(data1++);
8332  *(ptrd++) = 0;
8333  }
8334  break;
8335  default :
8336  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8337  *(ptrd++) = 0;
8338  *(ptrd++) = (unsigned char)*(data1++);
8339  *(ptrd++) = (unsigned char)*(data2++);
8340  *(ptrd++) = (unsigned char)*(data3++);
8341  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8342  *(ptrd++) = (unsigned char)*(data3++);
8343  *(ptrd++) = (unsigned char)*(data2++);
8344  *(ptrd++) = (unsigned char)*(data1++);
8345  *(ptrd++) = 0;
8346  }
8347  }
8348  }
8349  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; }
8350  }
8351  }
8352  } else {
8353  if (_normalization==3) {
8354  if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
8355  else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
8356  } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
8357  const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
8358  switch (cimg::X11_attr().nb_bits) {
8359  case 8 : { // 256 colormap, with normalization
8360  _set_colormap(_colormap,img._spectrum);
8361  unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:new unsigned char[(unsigned long)img._width*img._height];
8362  unsigned char *ptrd = (unsigned char*)ndata;
8363  switch (img._spectrum) {
8364  case 1 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8365  const unsigned char R = (unsigned char)((*(data1++)-_min)*mm);
8366  *(ptrd++) = R;
8367  } break;
8368  case 2 : for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8369  const unsigned char
8370  R = (unsigned char)((*(data1++)-_min)*mm),
8371  G = (unsigned char)((*(data2++)-_min)*mm);
8372  (*ptrd++) = (R&0xf0) | (G>>4);
8373  } break;
8374  default :
8375  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8376  const unsigned char
8377  R = (unsigned char)((*(data1++)-_min)*mm),
8378  G = (unsigned char)((*(data2++)-_min)*mm),
8379  B = (unsigned char)((*(data3++)-_min)*mm);
8380  *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
8381  }
8382  }
8383  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height); delete[] ndata; }
8384  } break;
8385  case 16 : { // 16 bits colors, with normalization
8386  unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:new unsigned short[(unsigned long)img._width*img._height];
8387  unsigned char *ptrd = (unsigned char*)ndata;
8388  const unsigned int M = 248;
8389  switch (img._spectrum) {
8390  case 1 :
8391  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8392  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2;
8393  *(ptrd++) = (val&M) | (G>>3);
8394  *(ptrd++) = (G<<5) | (val>>3);
8395  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8396  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm), G = val>>2;
8397  *(ptrd++) = (G<<5) | (val>>3);
8398  *(ptrd++) = (val&M) | (G>>3);
8399  }
8400  break;
8401  case 2 :
8402  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8403  const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8404  *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8405  *(ptrd++) = (G<<5);
8406  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8407  const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8408  *(ptrd++) = (G<<5);
8409  *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8410  }
8411  break;
8412  default :
8413  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8414  const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8415  *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8416  *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3);
8417  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8418  const unsigned char G = (unsigned char)((*(data2++)-_min)*mm)>>2;
8419  *(ptrd++) = (G<<5) | ((unsigned char)((*(data3++)-_min)*mm)>>3);
8420  *(ptrd++) = ((unsigned char)((*(data1++)-_min)*mm)&M) | (G>>3);
8421  }
8422  }
8423  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height); delete[] ndata; }
8424  } break;
8425  default : { // 24 bits colors, with normalization
8426  unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:new unsigned int[(unsigned long)img._width*img._height];
8427  if (sizeof(int)==4) { // 32 bits int uses optimized version
8428  unsigned int *ptrd = ndata;
8429  switch (img._spectrum) {
8430  case 1 :
8431  if (cimg::X11_attr().byte_order==cimg::endianness())
8432  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8433  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8434  *(ptrd++) = (val<<16) | (val<<8) | val;
8435  }
8436  else
8437  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8438  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8439  *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
8440  }
8441  break;
8442  case 2 :
8443  if (cimg::X11_attr().byte_order==cimg::endianness())
8444  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8445  *(ptrd++) =
8446  ((unsigned char)((*(data1++)-_min)*mm)<<16) |
8447  ((unsigned char)((*(data2++)-_min)*mm)<<8);
8448  else
8449  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8450  *(ptrd++) =
8451  ((unsigned char)((*(data2++)-_min)*mm)<<16) |
8452  ((unsigned char)((*(data1++)-_min)*mm)<<8);
8453  break;
8454  default :
8455  if (cimg::X11_attr().byte_order==cimg::endianness())
8456  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8457  *(ptrd++) =
8458  ((unsigned char)((*(data1++)-_min)*mm)<<16) |
8459  ((unsigned char)((*(data2++)-_min)*mm)<<8) |
8460  (unsigned char)((*(data3++)-_min)*mm);
8461  else
8462  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
8463  *(ptrd++) =
8464  ((unsigned char)((*(data3++)-_min)*mm)<<24) |
8465  ((unsigned char)((*(data2++)-_min)*mm)<<16) |
8466  ((unsigned char)((*(data1++)-_min)*mm)<<8);
8467  }
8468  } else {
8469  unsigned char *ptrd = (unsigned char*)ndata;
8470  switch (img._spectrum) {
8471  case 1 :
8472  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8473  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8474  (*ptrd++) = 0;
8475  (*ptrd++) = val;
8476  (*ptrd++) = val;
8477  (*ptrd++) = val;
8478  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8479  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
8480  (*ptrd++) = val;
8481  (*ptrd++) = val;
8482  (*ptrd++) = val;
8483  (*ptrd++) = 0;
8484  }
8485  break;
8486  case 2 :
8487  if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
8488  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8489  (*ptrd++) = 0;
8490  (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
8491  (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
8492  (*ptrd++) = 0;
8493  }
8494  break;
8495  default :
8496  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8497  (*ptrd++) = 0;
8498  (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
8499  (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
8500  (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm);
8501  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8502  (*ptrd++) = (unsigned char)((*(data3++)-_min)*mm);
8503  (*ptrd++) = (unsigned char)((*(data2++)-_min)*mm);
8504  (*ptrd++) = (unsigned char)((*(data1++)-_min)*mm);
8505  (*ptrd++) = 0;
8506  }
8507  }
8508  }
8509  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height); delete[] ndata; }
8510  }
8511  }
8512  }
8513  XUnlockDisplay(dpy);
8514  return *this;
8515  }
8516 
8517  template<typename T>
8518  const CImgDisplay& snapshot(CImg<T>& img) const {
8519  if (is_empty()) { img.assign(); return *this; }
8520  const unsigned char *ptrs = (unsigned char*)_data;
8521  img.assign(_width,_height,1,3);
8522  T
8523  *data1 = img.data(0,0,0,0),
8524  *data2 = img.data(0,0,0,1),
8525  *data3 = img.data(0,0,0,2);
8526  if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
8527  switch (cimg::X11_attr().nb_bits) {
8528  case 8 : {
8529  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8530  const unsigned char val = *(ptrs++);
8531  *(data1++) = (T)(val&0xe0);
8532  *(data2++) = (T)((val&0x1c)<<3);
8533  *(data3++) = (T)(val<<6);
8534  }
8535  } break;
8536  case 16 : {
8537  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8538  const unsigned char val0 = *(ptrs++), val1 = *(ptrs++);
8539  *(data1++) = (T)(val0&0xf8);
8540  *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
8541  *(data3++) = (T)(val1<<3);
8542  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8543  const unsigned short val0 = *(ptrs++), val1 = *(ptrs++);
8544  *(data1++) = (T)(val1&0xf8);
8545  *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
8546  *(data3++) = (T)(val0<<3);
8547  }
8548  } break;
8549  default : {
8550  if (cimg::X11_attr().byte_order) for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8551  ++ptrs;
8552  *(data1++) = (T)*(ptrs++);
8553  *(data2++) = (T)*(ptrs++);
8554  *(data3++) = (T)*(ptrs++);
8555  } else for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
8556  *(data3++) = (T)*(ptrs++);
8557  *(data2++) = (T)*(ptrs++);
8558  *(data1++) = (T)*(ptrs++);
8559  ++ptrs;
8560  }
8561  }
8562  }
8563  return *this;
8564  }
8565 
8566  // Windows-based implementation.
8567  //-------------------------------
8568 #elif cimg_display==2
8569 
8570  bool _is_mouse_tracked, _is_cursor_visible;
8571  HANDLE _thread, _is_created, _mutex;
8572  HWND _window, _background_window;
8573  CLIENTCREATESTRUCT _ccs;
8574  unsigned int *_data;
8575  DEVMODE _curr_mode;
8576  BITMAPINFO _bmi;
8577  HDC _hdc;
8578 
8579  static int screen_width() {
8580  DEVMODE mode;
8581  mode.dmSize = sizeof(DEVMODE);
8582  mode.dmDriverExtra = 0;
8583  EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
8584  return mode.dmPelsWidth;
8585  }
8586 
8587  static int screen_height() {
8588  DEVMODE mode;
8589  mode.dmSize = sizeof(DEVMODE);
8590  mode.dmDriverExtra = 0;
8591  EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
8592  return mode.dmPelsHeight;
8593  }
8594 
8595  static void wait_all() {
8596  WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
8597  }
8598 
8599  static LRESULT APIENTRY _handle_events(HWND window,UINT msg,WPARAM wParam,LPARAM lParam) {
8600 #ifdef _WIN64
8601  CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
8602 #else
8603  CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
8604 #endif
8605  MSG st_msg;
8606  switch (msg) {
8607  case WM_CLOSE :
8608  disp->_mouse_x = disp->_mouse_y = -1;
8609  disp->_window_x = disp->_window_y = 0;
8610  disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
8611  ReleaseMutex(disp->_mutex);
8612  ShowWindow(disp->_window,SW_HIDE);
8613  disp->_is_event = true;
8614  SetEvent(cimg::Win32_attr().wait_event);
8615  return 0;
8616  case WM_SIZE : {
8617  while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
8618  WaitForSingleObject(disp->_mutex,INFINITE);
8619  const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
8620  if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
8621  disp->_window_width = nw;
8622  disp->_window_height = nh;
8623  disp->_mouse_x = disp->_mouse_y = -1;
8624  disp->_is_resized = disp->_is_event = true;
8625  SetEvent(cimg::Win32_attr().wait_event);
8626  }
8627  ReleaseMutex(disp->_mutex);
8628  } break;
8629  case WM_MOVE : {
8630  while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
8631  WaitForSingleObject(disp->_mutex,INFINITE);
8632  const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
8633  if (nx!=disp->_window_x || ny!=disp->_window_y) {
8634  disp->_window_x = nx;
8635  disp->_window_y = ny;
8636  disp->_is_moved = disp->_is_event = true;
8637  SetEvent(cimg::Win32_attr().wait_event);
8638  }
8639  ReleaseMutex(disp->_mutex);
8640  } break;
8641  case WM_PAINT :
8642  disp->paint();
8643  break;
8644  case WM_KEYDOWN :
8645  disp->set_key((unsigned int)wParam);
8646  SetEvent(cimg::Win32_attr().wait_event);
8647  break;
8648  case WM_KEYUP :
8649  disp->set_key((unsigned int)wParam,false);
8650  SetEvent(cimg::Win32_attr().wait_event);
8651  break;
8652  case WM_MOUSEMOVE : {
8653  while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
8654  disp->_mouse_x = LOWORD(lParam);
8655  disp->_mouse_y = HIWORD(lParam);
8656 #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
8657  if (!disp->_is_mouse_tracked) {
8658  TRACKMOUSEEVENT tme;
8659  tme.cbSize = sizeof(TRACKMOUSEEVENT);
8660  tme.dwFlags = TME_LEAVE;
8661  tme.hwndTrack = disp->_window;
8662  if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
8663  }
8664 #endif
8665  if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
8666  disp->_mouse_x = disp->_mouse_y = -1;
8667  disp->_is_event = true;
8668  SetEvent(cimg::Win32_attr().wait_event);
8669  } break;
8670  case WM_MOUSELEAVE : {
8671  disp->_mouse_x = disp->_mouse_y = -1;
8672  disp->_is_mouse_tracked = false;
8673  } break;
8674  case WM_LBUTTONDOWN :
8675  disp->set_button(1);
8676  SetEvent(cimg::Win32_attr().wait_event);
8677  break;
8678  case WM_RBUTTONDOWN :
8679  disp->set_button(2);
8680  SetEvent(cimg::Win32_attr().wait_event);
8681  break;
8682  case WM_MBUTTONDOWN :
8683  disp->set_button(3);
8684  SetEvent(cimg::Win32_attr().wait_event);
8685  break;
8686  case WM_LBUTTONUP :
8687  disp->set_button(1,false);
8688  SetEvent(cimg::Win32_attr().wait_event);
8689  break;
8690  case WM_RBUTTONUP :
8691  disp->set_button(2,false);
8692  SetEvent(cimg::Win32_attr().wait_event);
8693  break;
8694  case WM_MBUTTONUP :
8695  disp->set_button(3,false);
8696  SetEvent(cimg::Win32_attr().wait_event);
8697  break;
8698  case 0x020A : // WM_MOUSEWHEEL:
8699  disp->set_wheel((int)((short)HIWORD(wParam))/120);
8700  SetEvent(cimg::Win32_attr().wait_event);
8701  case WM_SETCURSOR :
8702  if (disp->_is_cursor_visible) ShowCursor(TRUE);
8703  else ShowCursor(FALSE);
8704  break;
8705  }
8706  return DefWindowProc(window,msg,wParam,lParam);
8707  }
8708 
8709  static DWORD WINAPI _events_thread(void* arg) {
8710  CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
8711  const char *const title = (const char*)(((void**)arg)[1]);
8712  MSG msg;
8713  delete[] (void**)arg;
8714  disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
8715  disp->_bmi.bmiHeader.biWidth = disp->width();
8716  disp->_bmi.bmiHeader.biHeight = -disp->height();
8717  disp->_bmi.bmiHeader.biPlanes = 1;
8718  disp->_bmi.bmiHeader.biBitCount = 32;
8719  disp->_bmi.bmiHeader.biCompression = BI_RGB;
8720  disp->_bmi.bmiHeader.biSizeImage = 0;
8721  disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
8722  disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
8723  disp->_bmi.bmiHeader.biClrUsed = 0;
8724  disp->_bmi.bmiHeader.biClrImportant = 0;
8725  disp->_data = new unsigned int[(unsigned long)disp->_width*disp->_height];
8726  if (!disp->_is_fullscreen) { // Normal window
8727  RECT rect;
8728  rect.left = rect.top = 0; rect.right = disp->_width-1; rect.bottom = disp->_height-1;
8729  AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8730  const int
8731  border1 = (rect.right - rect.left + 1 - disp->_width)/2,
8732  border2 = rect.bottom - rect.top + 1 - disp->_height - border1;
8733  disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
8734  WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE), CW_USEDEFAULT,CW_USEDEFAULT,
8735  disp->_width + 2*border1, disp->_height + border1 + border2,
8736  0,0,0,&(disp->_ccs));
8737  if (!disp->_is_closed) {
8738  GetWindowRect(disp->_window,&rect);
8739  disp->_window_x = rect.left + border1;
8740  disp->_window_y = rect.top + border2;
8741  } else disp->_window_x = disp->_window_y = 0;
8742  } else { // Fullscreen window
8743  const unsigned int sx = screen_width(), sy = screen_height();
8744  disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
8745  WS_POPUP | (disp->_is_closed?0:WS_VISIBLE), (sx-disp->_width)/2, (sy-disp->_height)/2,
8746  disp->_width,disp->_height,0,0,0,&(disp->_ccs));
8747  disp->_window_x = disp->_window_y = 0;
8748  }
8749  SetForegroundWindow(disp->_window);
8750  disp->_hdc = GetDC(disp->_window);
8751  disp->_window_width = disp->_width;
8752  disp->_window_height = disp->_height;
8753  disp->flush();
8754 #ifdef _WIN64
8755  SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
8756  SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
8757 #else
8758  SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
8759  SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
8760 #endif
8761  SetEvent(disp->_is_created);
8762  while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
8763  return 0;
8764  }
8765 
8766  CImgDisplay& _update_window_pos() {
8767  if (_is_closed) _window_x = _window_y = -1;
8768  else {
8769  RECT rect;
8770  rect.left = rect.top = 0; rect.right = _width-1; rect.bottom = _height-1;
8771  AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8772  const int
8773  border1 = (rect.right - rect.left + 1 - _width)/2,
8774  border2 = rect.bottom - rect.top + 1 - _height - border1;
8775  GetWindowRect(_window,&rect);
8776  _window_x = rect.left + border1;
8777  _window_y = rect.top + border2;
8778  }
8779  return *this;
8780  }
8781 
8782  void _init_fullscreen() {
8783  _background_window = 0;
8784  if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
8785  else {
8786  DEVMODE mode;
8787  unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
8788  for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
8789  const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
8790  if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
8791  bestbpp = mode.dmBitsPerPel;
8792  ibest = imode;
8793  bw = nw; bh = nh;
8794  }
8795  }
8796  if (bestbpp) {
8797  _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
8798  EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
8799  EnumDisplaySettings(0,ibest,&mode);
8800  ChangeDisplaySettings(&mode,0);
8801  } else _curr_mode.dmSize = 0;
8802 
8803  const unsigned int sx = screen_width(), sy = screen_height();
8804  if (sx!=_width || sy!=_height) {
8805  CLIENTCREATESTRUCT background_ccs;
8806  _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE, 0,0,sx,sy,0,0,0,&background_ccs);
8807  SetForegroundWindow(_background_window);
8808  }
8809  }
8810  }
8811 
8812  void _desinit_fullscreen() {
8813  if (!_is_fullscreen) return;
8814  if (_background_window) DestroyWindow(_background_window);
8815  _background_window = 0;
8816  if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
8817  _is_fullscreen = false;
8818  }
8819 
8820  CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
8821  const unsigned int normalization_type=3,
8822  const bool fullscreen_flag=false, const bool closed_flag=false) {
8823 
8824  // Allocate space for window title
8825  const char *const nptitle = ptitle?ptitle:"";
8826  const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
8827  char *const tmp_title = s?new char[s]:0;
8828  if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
8829 
8830  // Destroy previous window if existing
8831  if (!is_empty()) assign();
8832 
8833  // Set display variables
8834  _width = cimg::min(dimw,(unsigned int)screen_width());
8835  _height = cimg::min(dimh,(unsigned int)screen_height());
8836  _normalization = normalization_type<4?normalization_type:3;
8837  _is_fullscreen = fullscreen_flag;
8838  _window_x = _window_y = 0;
8839  _is_closed = closed_flag;
8840  _is_cursor_visible = true;
8841  _is_mouse_tracked = false;
8842  _title = tmp_title;
8843  flush();
8844  if (_is_fullscreen) _init_fullscreen();
8845 
8846  // Create event thread
8847  void *const arg = (void*)(new void*[2]);
8848  ((void**)arg)[0] = (void*)this;
8849  ((void**)arg)[1] = (void*)_title;
8850  unsigned long ThreadID = 0;
8851  _mutex = CreateMutex(0,FALSE,0);
8852  _is_created = CreateEvent(0,FALSE,FALSE,0);
8853  _thread = CreateThread(0,0,_events_thread,arg,0,&ThreadID);
8854  WaitForSingleObject(_is_created,INFINITE);
8855  return *this;
8856  }
8857 
8858  CImgDisplay& assign() {
8859  if (is_empty()) return flush();
8860  DestroyWindow(_window);
8861  TerminateThread(_thread,0);
8862  delete[] _data;
8863  delete[] _title;
8864  _data = 0;
8865  _title = 0;
8866  if (_is_fullscreen) _desinit_fullscreen();
8867  _width = _height = _normalization = _window_width = _window_height = 0;
8868  _window_x = _window_y = 0;
8869  _is_fullscreen = false;
8870  _is_closed = true;
8871  _min = _max = 0;
8872  _title = 0;
8873  flush();
8874  return *this;
8875  }
8876 
8877  CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
8878  const unsigned int normalization_type=3,
8879  const bool fullscreen_flag=false, const bool closed_flag=false) {
8880  if (!dimw || !dimh) return assign();
8881  _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
8882  _min = _max = 0;
8883  std::memset(_data,0,sizeof(unsigned int)*_width*_height);
8884  return paint();
8885  }
8886 
8887  template<typename T>
8888  CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
8889  const unsigned int normalization_type=3,
8890  const bool fullscreen_flag=false, const bool closed_flag=false) {
8891  if (!img) return assign();
8892  CImg<T> tmp;
8893  const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8894  _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8895  if (_normalization==2) _min = (float)nimg.min_max(_max);
8896  return display(nimg);
8897  }
8898 
8899  template<typename T>
8900  CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
8901  const unsigned int normalization_type=3,
8902  const bool fullscreen_flag=false, const bool closed_flag=false) {
8903  if (!list) return assign();
8904  CImg<T> tmp;
8905  const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d(img._width/2,img._height/2,img._depth/2));
8906  _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
8907  if (_normalization==2) _min = (float)nimg.min_max(_max);
8908  return display(nimg);
8909  }
8910 
8911  CImgDisplay& assign(const CImgDisplay& disp) {
8912  if (!disp) return assign();
8913  _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
8914  std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
8915  return paint();
8916  }
8917 
8918  CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
8919  if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
8920  if (is_empty()) return assign(nwidth,nheight);
8921  const unsigned int
8922  tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
8923  tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
8924  dimx = tmpdimx?tmpdimx:1,
8925  dimy = tmpdimy?tmpdimy:1;
8926  if (_window_width!=dimx || _window_height!=dimy) {
8927  RECT rect; rect.left = rect.top = 0; rect.right = dimx - 1; rect.bottom = dimy - 1;
8928  AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8929  const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
8930  SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
8931  }
8932  if (_width!=dimx || _height!=dimy) {
8933  unsigned int *const ndata = new unsigned int[dimx*dimy];
8934  if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
8935  else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
8936  delete[] _data;
8937  _data = ndata;
8938  _bmi.bmiHeader.biWidth = dimx;
8939  _bmi.bmiHeader.biHeight = -(int)dimy;
8940  _width = dimx;
8941  _height = dimy;
8942  }
8943  _window_width = dimx; _window_height = dimy;
8944  _is_resized = false;
8945  if (_is_fullscreen) move((screen_width()-_width)/2,(screen_height()-_height)/2);
8946  if (force_redraw) return paint();
8947  return *this;
8948  }
8949 
8950  CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
8951  if (is_empty()) return *this;
8952  if (force_redraw) {
8953  const unsigned long buf_size = _width*_height*4UL;
8954  void *odata = std::malloc(buf_size);
8955  std::memcpy(odata,_data,buf_size);
8956  assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8957  std::memcpy(_data,odata,buf_size);
8958  std::free(odata);
8959  return paint();
8960  }
8961  return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
8962  }
8963 
8964  CImgDisplay& show() {
8965  if (is_empty() || !_is_closed) return *this;
8966  _is_closed = false;
8967  if (_is_fullscreen) _init_fullscreen();
8968  ShowWindow(_window,SW_SHOW);
8969  _update_window_pos();
8970  return paint();
8971  }
8972 
8973  CImgDisplay& close() {
8974  if (is_empty() || _is_closed) return *this;
8975  _is_closed = true;
8976  if (_is_fullscreen) _desinit_fullscreen();
8977  ShowWindow(_window,SW_HIDE);
8978  _window_x = _window_y = 0;
8979  return *this;
8980  }
8981 
8982  CImgDisplay& move(const int posx, const int posy) {
8983  if (is_empty()) return *this;
8984  if (!_is_fullscreen) {
8985  RECT rect; rect.left = rect.top = 0; rect.right = _window_width-1; rect.bottom = _window_height-1;
8986  AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
8987  const int border1 = (rect.right-rect.left+1-_width)/2, border2 = rect.bottom-rect.top+1-_height-border1;
8988  SetWindowPos(_window,0,posx-border1,posy-border2,0,0,SWP_NOSIZE | SWP_NOZORDER);
8989  } else SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
8990  _window_x = posx;
8991  _window_y = posy;
8992  _is_moved = false;
8993  return show();
8994  }
8995 
8996  CImgDisplay& show_mouse() {
8997  if (is_empty()) return *this;
8998  _is_cursor_visible = true;
8999  ShowCursor(TRUE);
9000  SendMessage(_window,WM_SETCURSOR,0,0);
9001  return *this;
9002  }
9003 
9004  CImgDisplay& hide_mouse() {
9005  if (is_empty()) return *this;
9006  _is_cursor_visible = false;
9007  ShowCursor(FALSE);
9008  SendMessage(_window,WM_SETCURSOR,0,0);
9009  return *this;
9010  }
9011 
9012  CImgDisplay& set_mouse(const int posx, const int posy) {
9013  if (_is_closed || posx<0 || posy<0) return *this;
9014  _update_window_pos();
9015  const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
9016  if (res) { _mouse_x = posx; _mouse_y = posy; }
9017  return *this;
9018  }
9019 
9020  CImgDisplay& set_title(const char *const format, ...) {
9021  if (is_empty()) return *this;
9022  char tmp[1024] = { 0 };
9023  va_list ap;
9024  va_start(ap, format);
9025  cimg_vsnprintf(tmp,sizeof(tmp),format,ap);
9026  va_end(ap);
9027  if (!std::strcmp(_title,tmp)) return *this;
9028  delete[] _title;
9029  const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
9030  _title = new char[s];
9031  std::memcpy(_title,tmp,s*sizeof(char));
9032  SetWindowTextA(_window, tmp);
9033  return *this;
9034  }
9035 
9036  template<typename T>
9037  CImgDisplay& display(const CImg<T>& img) {
9038  if (!img)
9039  throw CImgArgumentException(_cimgdisplay_instance
9040  "display(): Empty specified image.",
9041  cimgdisplay_instance);
9042  if (is_empty()) return assign(img);
9043  return render(img).paint();
9044  }
9045 
9046  CImgDisplay& paint() {
9047  if (_is_closed) return *this;
9048  WaitForSingleObject(_mutex,INFINITE);
9049  SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
9050  ReleaseMutex(_mutex);
9051  return *this;
9052  }
9053 
9054  template<typename T>
9055  CImgDisplay& render(const CImg<T>& img) {
9056  if (!img)
9057  throw CImgArgumentException(_cimgdisplay_instance
9058  "render(): Empty specified image.",
9059  cimgdisplay_instance);
9060 
9061  if (is_empty()) return *this;
9062  if (img._depth!=1) return render(img.get_projections2d(img._width/2,img._height/2,img._depth/2));
9063 
9064  const T
9065  *data1 = img._data,
9066  *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
9067  *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
9068 
9069  WaitForSingleObject(_mutex,INFINITE);
9070  unsigned int
9071  *const ndata = (img._width==_width && img._height==_height)?_data:new unsigned int[(unsigned long)img._width*img._height],
9072  *ptrd = ndata;
9073 
9074  if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
9075  _min = _max = 0;
9076  switch (img._spectrum) {
9077  case 1 : {
9078  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9079  const unsigned char val = (unsigned char)*(data1++);
9080  *(ptrd++) = (val<<16) | (val<<8) | val;
9081  }
9082  } break;
9083  case 2 : {
9084  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
9085  *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
9086  } break;
9087  default : {
9088  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy)
9089  *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) | (unsigned char)*(data3++);
9090  }
9091  }
9092  } else {
9093  if (_normalization==3) {
9094  if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
9095  else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
9096  } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
9097  const float delta = _max - _min, mm = 255/(delta?delta:1.0f);
9098  switch (img._spectrum) {
9099  case 1 : {
9100  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9101  const unsigned char val = (unsigned char)((*(data1++)-_min)*mm);
9102  *(ptrd++) = (val<<16) | (val<<8) | val;
9103  }
9104  } break;
9105  case 2 : {
9106  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9107  const unsigned char
9108  R = (unsigned char)((*(data1++)-_min)*mm),
9109  G = (unsigned char)((*(data2++)-_min)*mm);
9110  *(ptrd++) = (R<<16) | (G<<8);
9111  }
9112  } break;
9113  default : {
9114  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9115  const unsigned char
9116  R = (unsigned char)((*(data1++)-_min)*mm),
9117  G = (unsigned char)((*(data2++)-_min)*mm),
9118  B = (unsigned char)((*(data3++)-_min)*mm);
9119  *(ptrd++) = (R<<16) | (G<<8) | B;
9120  }
9121  }
9122  }
9123  }
9124  if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
9125  ReleaseMutex(_mutex);
9126  return *this;
9127  }
9128 
9129  template<typename T>
9130  const CImgDisplay& snapshot(CImg<T>& img) const {
9131  if (is_empty()) { img.assign(); return *this; }
9132  const unsigned int *ptrs = _data;
9133  img.assign(_width,_height,1,3);
9134  T
9135  *data1 = img.data(0,0,0,0),
9136  *data2 = img.data(0,0,0,1),
9137  *data3 = img.data(0,0,0,2);
9138  for (unsigned long xy = (unsigned long)img._width*img._height; xy>0; --xy) {
9139  const unsigned int val = *(ptrs++);
9140  *(data1++) = (T)(unsigned char)(val>>16);
9141  *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
9142  *(data3++) = (T)(unsigned char)(val&0xFF);
9143  }
9144  return *this;
9145  }
9146 #endif
9147 
9149  };
9150 
9151  /*
9152  #--------------------------------------
9153  #
9154  #
9155  #
9156  # Definition of the CImg<T> structure
9157  #
9158  #
9159  #
9160  #--------------------------------------
9161  */
9162 
9164 
9254  template<typename T>
9255  struct CImg {
9256 
9257  unsigned int _width, _height, _depth, _spectrum;
9258  bool _is_shared;
9259  T *_data;
9260 
9262 
9274  typedef T* iterator;
9275 
9277 
9290  typedef const T* const_iterator;
9291 
9293 
9300  typedef T value_type;
9301 
9302  // Define common types related to template type T.
9303  typedef typename cimg::superset<T,bool>::type Tbool;
9304  typedef typename cimg::superset<T,unsigned char>::type Tuchar;
9305  typedef typename cimg::superset<T,char>::type Tchar;
9306  typedef typename cimg::superset<T,unsigned short>::type Tushort;
9307  typedef typename cimg::superset<T,short>::type Tshort;
9308  typedef typename cimg::superset<T,unsigned int>::type Tuint;
9309  typedef typename cimg::superset<T,int>::type Tint;
9310  typedef typename cimg::superset<T,unsigned long>::type Tulong;
9311  typedef typename cimg::superset<T,long>::type Tlong;
9312  typedef typename cimg::superset<T,float>::type Tfloat;
9313  typedef typename cimg::superset<T,double>::type Tdouble;
9314  typedef typename cimg::last<T,bool>::type boolT;
9315  typedef typename cimg::last<T,unsigned char>::type ucharT;
9316  typedef typename cimg::last<T,char>::type charT;
9317  typedef typename cimg::last<T,unsigned short>::type ushortT;
9318  typedef typename cimg::last<T,short>::type shortT;
9319  typedef typename cimg::last<T,unsigned int>::type uintT;
9320  typedef typename cimg::last<T,int>::type intT;
9321  typedef typename cimg::last<T,unsigned long>::type ulongT;
9322  typedef typename cimg::last<T,long>::type longT;
9323  typedef typename cimg::last<T,float>::type floatT;
9324  typedef typename cimg::last<T,double>::type doubleT;
9325 
9327  //---------------------------
9328  //
9330 
9331  //---------------------------
9332 #ifdef cimg_plugin
9333 #include cimg_plugin
9334 #endif
9335 #ifdef cimg_plugin1
9336 #include cimg_plugin1
9337 #endif
9338 #ifdef cimg_plugin2
9339 #include cimg_plugin2
9340 #endif
9341 #ifdef cimg_plugin3
9342 #include cimg_plugin3
9343 #endif
9344 #ifdef cimg_plugin4
9345 #include cimg_plugin4
9346 #endif
9347 #ifdef cimg_plugin5
9348 #include cimg_plugin5
9349 #endif
9350 #ifdef cimg_plugin6
9351 #include cimg_plugin6
9352 #endif
9353 #ifdef cimg_plugin7
9354 #include cimg_plugin7
9355 #endif
9356 #ifdef cimg_plugin8
9357 #include cimg_plugin8
9358 #endif
9359 
9361  //---------------------------------------------------------
9362  //
9364 
9365  //---------------------------------------------------------
9366 
9368 
9376  ~CImg() {
9377  if (!_is_shared) delete[] _data;
9378  }
9379 
9381 
9396  CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
9397 
9399 
9418  explicit CImg(const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1):
9419  _is_shared(false) {
9420  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9421  if (siz) {
9422  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9423  try { _data = new T[siz]; } catch (...) {
9424  _width = _height = _depth = _spectrum = 0; _data = 0;
9425  throw CImgInstanceException(_cimg_instance
9426  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9427  cimg_instance,
9428  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9429  }
9430  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9431  }
9432 
9434 
9447  CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value):
9448  _is_shared(false) {
9449  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9450  if (siz) {
9451  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9452  try { _data = new T[siz]; } catch (...) {
9453  _width = _height = _depth = _spectrum = 0; _data = 0;
9454  throw CImgInstanceException(_cimg_instance
9455  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9456  cimg_instance,
9457  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9458  }
9459  fill(value);
9460  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9461  }
9462 
9464 
9490  CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9491  const int value0, const int value1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9492 #define _CImg_stdarg(img,a0,a1,N,t) { \
9493  unsigned long _siz = (unsigned long)N; \
9494  if (_siz--) { \
9495  va_list ap; \
9496  va_start(ap,a1); \
9497  T *ptrd = (img)._data; \
9498  *(ptrd++) = (T)a0; \
9499  if (_siz--) { \
9500  *(ptrd++) = (T)a1; \
9501  for (; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
9502  } \
9503  va_end(ap); \
9504  } \
9505  }
9506  assign(size_x,size_y,size_z,size_c);
9507  _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int);
9508  }
9509 
9511 
9532  CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9533  const double value0, const double value1, ...):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9534  assign(size_x,size_y,size_z,size_c);
9535  _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,double);
9536  }
9537 
9539 
9566  CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9567  const char *const values, const bool repeat_values):_is_shared(false) {
9568  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9569  if (siz) {
9570  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9571  try { _data = new T[siz]; } catch (...) {
9572  _width = _height = _depth = _spectrum = 0; _data = 0;
9573  throw CImgInstanceException(_cimg_instance
9574  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9575  cimg_instance,
9576  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9577  }
9578  fill(values,repeat_values);
9579  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9580  }
9581 
9583 
9609  template<typename t>
9610  CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
9611  const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
9612  if (is_shared) {
9613  _width = _height = _depth = _spectrum = 0; _data = 0;
9614  throw CImgArgumentException(_cimg_instance
9615  "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance from a (%s*) buffer "
9616  "(pixel types are different).",
9617  cimg_instance,
9618  size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
9619  }
9620  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9621  if (values && siz) {
9622  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9623  try { _data = new T[siz]; } catch (...) {
9624  _width = _height = _depth = _spectrum = 0; _data = 0;
9625  throw CImgInstanceException(_cimg_instance
9626  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9627  cimg_instance,
9628  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9629 
9630  }
9631  const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9632  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9633  }
9634 
9636  CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
9637  const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
9638  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9639  if (values && siz) {
9640  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
9641  if (_is_shared) _data = const_cast<T*>(values);
9642  else {
9643  try { _data = new T[siz]; } catch (...) {
9644  _width = _height = _depth = _spectrum = 0; _data = 0;
9645  throw CImgInstanceException(_cimg_instance
9646  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9647  cimg_instance,
9648  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9649  }
9650  std::memcpy(_data,values,siz*sizeof(T)); }
9651  } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9652  }
9653 
9655 
9673  explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9674  assign(filename);
9675  }
9676 
9678 
9692  template<typename t>
9693  CImg(const CImg<t>& img):_is_shared(false) {
9694  const unsigned long siz = img.size();
9695  if (img._data && siz) {
9696  _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9697  try { _data = new T[siz]; } catch (...) {
9698  _width = _height = _depth = _spectrum = 0; _data = 0;
9699  throw CImgInstanceException(_cimg_instance
9700  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9701  cimg_instance,
9702  cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9703  img._width,img._height,img._depth,img._spectrum);
9704  }
9705  const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9706  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9707  }
9708 
9710  CImg(const CImg<T>& img) {
9711  const unsigned long siz = img.size();
9712  if (img._data && siz) {
9713  _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = img._is_shared;
9714  if (_is_shared) _data = const_cast<T*>(img._data);
9715  else {
9716  try { _data = new T[siz]; } catch (...) {
9717  _width = _height = _depth = _spectrum = 0; _data = 0;
9718  throw CImgInstanceException(_cimg_instance
9719  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9720  cimg_instance,
9721  cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9722  img._width,img._height,img._depth,img._spectrum);
9723 
9724  }
9725  std::memcpy(_data,img._data,siz*sizeof(T));
9726  }
9727  } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9728  }
9729 
9731 
9745  template<typename t>
9746  CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
9747  if (is_shared) {
9748  _width = _height = _depth = _spectrum = 0; _data = 0;
9749  throw CImgArgumentException(_cimg_instance
9750  "CImg(): Invalid construction request of a shared instance from a "
9751  "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
9752  cimg_instance,
9753  CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
9754  }
9755  const unsigned long siz = img.size();
9756  if (img._data && siz) {
9757  _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
9758  try { _data = new T[siz]; } catch (...) {
9759  _width = _height = _depth = _spectrum = 0; _data = 0;
9760  throw CImgInstanceException(_cimg_instance
9761  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9762  cimg_instance,
9763  cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9764  img._width,img._height,img._depth,img._spectrum);
9765  }
9766  const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9767  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
9768  }
9769 
9771  CImg(const CImg<T>& img, const bool is_shared) {
9772  const unsigned long siz = img.size();
9773  if (img._data && siz) {
9774  _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum; _is_shared = is_shared;
9775  if (_is_shared) _data = const_cast<T*>(img._data);
9776  else {
9777  try { _data = new T[siz]; } catch (...) {
9778  _width = _height = _depth = _spectrum = 0; _data = 0;
9779  throw CImgInstanceException(_cimg_instance
9780  "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9781  cimg_instance,
9782  cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
9783  img._width,img._height,img._depth,img._spectrum);
9784  }
9785  std::memcpy(_data,img._data,siz*sizeof(T));
9786  }
9787  } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
9788  }
9789 
9791 
9808  template<typename t>
9809  CImg(const CImg<t>& img, const char *const dimensions):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9810  assign(img,dimensions);
9811  }
9812 
9814 
9823  template<typename t>
9824  CImg(const CImg<t>& img, const char *const dimensions, const T value):
9825  _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9826  assign(img,dimensions).fill(value);
9827  }
9828 
9830 
9838  explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
9839  disp.snapshot(*this);
9840  }
9841 
9843 
9847  if (!_is_shared) delete[] _data;
9848  _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
9849  return *this;
9850  }
9851 
9853 
9856  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1, const unsigned int size_z=1, const unsigned int size_c=1) {
9857  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9858  if (!siz) return assign();
9859  const unsigned long curr_siz = size();
9860  if (siz!=curr_siz) {
9861  if (_is_shared)
9862  throw CImgArgumentException(_cimg_instance
9863  "assign(): Invalid assignement request of shared instance from specified image (%u,%u,%u,%u).",
9864  cimg_instance,
9865  size_x,size_y,size_z,size_c);
9866  else {
9867  delete[] _data;
9868  try { _data = new T[siz]; } catch (...) {
9869  _width = _height = _depth = _spectrum = 0; _data = 0;
9870  throw CImgInstanceException(_cimg_instance
9871  "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9872  cimg_instance,
9873  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9874  }
9875  }
9876  }
9877  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9878  return *this;
9879  }
9880 
9882 
9885  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c, const T value) {
9886  return assign(size_x,size_y,size_z,size_c).fill(value);
9887  }
9888 
9890 
9893  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9894  const int value0, const int value1, ...) {
9895  assign(size_x,size_y,size_z,size_c);
9896  _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,int);
9897  return *this;
9898  }
9899 
9901 
9904  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9905  const double value0, const double value1, ...) {
9906  assign(size_x,size_y,size_z,size_c);
9907  _CImg_stdarg(*this,value0,value1,(unsigned long)size_x*size_y*size_z*size_c,double);
9908  return *this;
9909  }
9910 
9912 
9915  CImg<T>& assign(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
9916  const char *const values, const bool repeat_values) {
9917  return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
9918  }
9919 
9921 
9924  template<typename t>
9925  CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
9926  const unsigned int size_z=1, const unsigned int size_c=1) {
9927  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9928  if (!values || !siz) return assign();
9929  assign(size_x,size_y,size_z,size_c);
9930  const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
9931  return *this;
9932  }
9933 
9935  CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
9936  const unsigned int size_z=1, const unsigned int size_c=1) {
9937  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9938  if (!values || !siz) return assign();
9939  const unsigned long curr_siz = size();
9940  if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
9941  if (_is_shared || values+siz<_data || values>=_data+size()) {
9942  assign(size_x,size_y,size_z,size_c);
9943  if (_is_shared) std::memmove(_data,values,siz*sizeof(T));
9944  else std::memcpy(_data,values,siz*sizeof(T));
9945  } else {
9946  T *new_data = 0;
9947  try { new_data = new T[siz]; } catch (...) {
9948  _width = _height = _depth = _spectrum = 0; _data = 0;
9949  throw CImgInstanceException(_cimg_instance
9950  "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
9951  cimg_instance,
9952  cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),size_x,size_y,size_z,size_c);
9953  }
9954  std::memcpy(new_data,values,siz*sizeof(T));
9955  delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
9956  }
9957  return *this;
9958  }
9959 
9961  template<typename t>
9962  CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
9963  const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
9964  if (is_shared)
9965  throw CImgArgumentException(_cimg_instance
9966  "assign(): Invalid assignment request of shared instance from (%s*) buffer"
9967  "(pixel types are different).",
9968  cimg_instance,
9970  return assign(values,size_x,size_y,size_z,size_c);
9971  }
9972 
9974  CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
9975  const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
9976  const unsigned long siz = (unsigned long)size_x*size_y*size_z*size_c;
9977  if (!values || !siz) {
9978  if (is_shared)
9979  throw CImgArgumentException(_cimg_instance
9980  "assign(): Invalid assignment request of shared instance from (null) or empty buffer.",
9981  cimg_instance);
9982  else return assign();
9983  }
9984  if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
9985  else {
9986  if (!_is_shared) {
9987  if (values+siz<_data || values>=_data+size()) assign();
9988  else cimg::warn(_cimg_instance
9989  "assign(): Shared image instance has overlapping memory.",
9990  cimg_instance);
9991  }
9992  _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
9993  _data = const_cast<T*>(values);
9994  }
9995  return *this;
9996  }
9997 
9999 
10002  CImg<T>& assign(const char *const filename) {
10003  return load(filename);
10004  }
10005 
10007 
10010  template<typename t>
10011  CImg<T>& assign(const CImg<t>& img) {
10012  return assign(img._data,img._width,img._height,img._depth,img._spectrum);
10013  }
10014 
10016 
10019  template<typename t>
10020  CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
10021  return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
10022  }
10023 
10025 
10028  template<typename t>
10029  CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
10030  if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
10031  unsigned int siz[4] = { 0,1,1,1 }, k = 0;
10032  for (const char *s = dimensions; *s && k<4; ++k) {
10033  char item[256] = { 0 };
10034  if (std::sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item)>0) s+=std::strlen(item);
10035  if (*s) {
10036  unsigned int val = 0; char sep = 0;
10037  if (std::sscanf(s,"%u%c",&val,&sep)>0) {
10038  if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
10039  else siz[k] = val;
10040  while (*s>='0' && *s<='9') ++s; if (sep=='%') ++s;
10041  } else switch (cimg::uncase(*s)) {
10042  case 'x' : case 'w' : siz[k] = img._width; ++s; break;
10043  case 'y' : case 'h' : siz[k] = img._height; ++s; break;
10044  case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
10045  case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
10046  default :
10047  throw CImgArgumentException(_cimg_instance
10048  "assign(): Invalid character '%c' detected in specified dimension string '%s'.",
10049  cimg_instance,
10050  *s,dimensions);
10051  }
10052  }
10053  }
10054  return assign(siz[0],siz[1],siz[2],siz[3]);
10055  }
10056 
10058 
10061  template<typename t>
10062  CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T value) {
10063  return assign(img,dimensions).fill(value);
10064  }
10065 
10067 
10070  CImg<T>& assign(const CImgDisplay &disp) {
10071  disp.snapshot(*this);
10072  return *this;
10073  }
10074 
10076 
10082  return assign();
10083  }
10084 
10086 
10101  template<typename t>
10103  img.assign(*this);
10104  assign();
10105  return img;
10106  }
10107 
10110  if (_is_shared || img._is_shared) img.assign(*this);
10111  else swap(img);
10112  assign();
10113  return img;
10114  }
10115 
10117 
10134  template<typename t>
10135  CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
10136  const unsigned int npos = pos>list._width?list._width:pos;
10137  move_to(list.insert(1,npos)[npos]);
10138  return list;
10139  }
10140 
10142 
10155  cimg::swap(_width,img._width);
10156  cimg::swap(_height,img._height);
10157  cimg::swap(_depth,img._depth);
10158  cimg::swap(_spectrum,img._spectrum);
10159  cimg::swap(_data,img._data);
10160  cimg::swap(_is_shared,img._is_shared);
10161  return img;
10162  }
10163 
10165 
10172  static CImg<T>& empty() {
10173  static CImg<T> _empty;
10174  return _empty.assign();
10175  }
10176 
10178  //------------------------------------------
10179  //
10181 
10182  //------------------------------------------
10183 
10185 
10214 #if cimg_verbosity>=3
10215  T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
10216  const unsigned long off = (unsigned long)offset(x,y,z,c);
10217  if (!_data || off>=size()) {
10218  cimg::warn(_cimg_instance
10219  "operator(): Invalid pixel request, at coordinates (%u,%u,%u,%u) [offset=%u].",
10220  cimg_instance,
10221  x,y,z,c,off);
10222  return *_data;
10223  }
10224  else return _data[off];
10225  }
10226 
10228  const T& operator()(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
10229  return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
10230  }
10231 
10233 
10245  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10246  const unsigned long wh, const unsigned long whd=0) {
10247  cimg::unused(wh,whd);
10248  return (*this)(x,y,z,c);
10249  }
10250 
10252  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10253  const unsigned long wh, const unsigned long whd=0) const {
10254  cimg::unused(wh,whd);
10255  return (*this)(x,y,z,c);
10256  }
10257 #else
10258  T& operator()(const unsigned int x) {
10259  return _data[x];
10260  }
10261 
10262  const T& operator()(const unsigned int x) const {
10263  return _data[x];
10264  }
10265 
10266  T& operator()(const unsigned int x, const unsigned int y) {
10267  return _data[x + y*_width];
10268  }
10269 
10270  const T& operator()(const unsigned int x, const unsigned int y) const {
10271  return _data[x + y*_width];
10272  }
10273 
10274  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
10275  return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height];
10276  }
10277 
10278  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
10279  return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height];
10280  }
10281 
10282  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
10283  return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth];
10284  }
10285 
10286  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
10287  return _data[x + y*(unsigned long)_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth];
10288  }
10289 
10290  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
10291  const unsigned long wh) {
10292  return _data[x + y*_width + z*wh];
10293  }
10294 
10295  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
10296  const unsigned long wh) const {
10297  return _data[x + y*_width + z*wh];
10298  }
10299 
10300  T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10301  const unsigned long wh, const unsigned long whd) {
10302  return _data[x + y*_width + z*wh + c*whd];
10303  }
10304 
10305  const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
10306  const unsigned long wh, const unsigned long whd) const {
10307  return _data[x + y*_width + z*wh + c*whd];
10308  }
10309 #endif
10310 
10312 
10333  operator T*() {
10334  return _data;
10335  }
10336 
10338  operator const T*() const {
10339  return _data;
10340  }
10341 
10343 
10356  CImg<T>& operator=(const T value) {
10357  return fill(value);
10358  }
10359 
10361 
10380  CImg<T>& operator=(const char *const expression) {
10381  const unsigned int omode = cimg::exception_mode();
10382  cimg::exception_mode() = 0;
10383  try {
10384  fill(expression,true);
10385  } catch (CImgException&) {
10386  cimg::exception_mode() = omode;
10387  load(expression);
10388  }
10389  cimg::exception_mode() = omode;
10390  return *this;
10391  }
10392 
10394 
10397  template<typename t>
10398  CImg<T>& operator=(const CImg<t>& img) {
10399  return assign(img);
10400  }
10401 
10403  CImg<T>& operator=(const CImg<T>& img) {
10404  return assign(img);
10405  }
10406 
10408 
10412  disp.snapshot(*this);
10413  return *this;
10414  }
10415 
10417 
10441  template<typename t>
10442  CImg<T>& operator+=(const t value) {
10443  cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd + value);
10444  return *this;
10445  }
10446 
10448 
10455  CImg<T>& operator+=(const char *const expression) {
10456  const unsigned int omode = cimg::exception_mode();
10457  cimg::exception_mode() = 0;
10458  try {
10459  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10460  _cimg_math_parser mp(base,expression,"operator+=");
10461  T *ptrd = _data;
10462  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd + mp.eval(x,y,z,c)); ++ptrd; }
10463  } catch (CImgException&) {
10464  cimg::exception_mode() = omode;
10465  *this+=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10466  }
10467  cimg::exception_mode() = omode;
10468  return *this;
10469  }
10470 
10472 
10491  template<typename t>
10492  CImg<T>& operator+=(const CImg<t>& img) {
10493  const unsigned long siz = size(), isiz = img.size();
10494  if (siz && isiz) {
10495  if (is_overlapped(img)) return *this+=+img;
10496  T *ptrd = _data, *const ptre = _data + siz;
10497  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10498  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
10499  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
10500  }
10501  return *this;
10502  }
10503 
10505 
10511  cimg_for(*this,ptrd,T) ++*ptrd;
10512  return *this;
10513  }
10514 
10516 
10523  const CImg<T> copy(*this,false);
10524  ++*this;
10525  return copy;
10526  }
10527 
10529 
10537  CImg<T> operator+() const {
10538  return CImg<T>(*this,false);
10539  }
10540 
10542 
10546  template<typename t>
10547  CImg<_cimg_Tt> operator+(const t value) const {
10548  return CImg<_cimg_Tt>(*this,false)+=value;
10549  }
10550 
10552 
10556  CImg<Tfloat> operator+(const char *const expression) const {
10557  return CImg<Tfloat>(*this,false)+=expression;
10558  }
10559 
10561 
10565  template<typename t>
10566  CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
10567  return CImg<_cimg_Tt>(*this,false)+=img;
10568  }
10569 
10571 
10574  template<typename t>
10575  CImg<T>& operator-=(const t value) {
10576  cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd - value);
10577  return *this;
10578  }
10579 
10581 
10584  CImg<T>& operator-=(const char *const expression) {
10585  const unsigned int omode = cimg::exception_mode();
10586  cimg::exception_mode() = 0;
10587  try {
10588  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10589  _cimg_math_parser mp(base,expression,"operator-=");
10590  T *ptrd = _data;
10591  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd - mp.eval(x,y,z,c)); ++ptrd; }
10592  } catch (CImgException&) {
10593  cimg::exception_mode() = omode;
10594  *this-=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10595  }
10596  cimg::exception_mode() = omode;
10597  return *this;
10598  }
10599 
10601 
10604  template<typename t>
10605  CImg<T>& operator-=(const CImg<t>& img) {
10606  const unsigned long siz = size(), isiz = img.size();
10607  if (siz && isiz) {
10608  if (is_overlapped(img)) return *this-=+img;
10609  T *ptrd = _data, *const ptre = _data + siz;
10610  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10611  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
10612  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
10613  }
10614  return *this;
10615  }
10616 
10618 
10622  cimg_for(*this,ptrd,T) *ptrd = *ptrd-(T)1;
10623  return *this;
10624  }
10625 
10627 
10631  const CImg<T> copy(*this,false);
10632  --*this;
10633  return copy;
10634  }
10635 
10637 
10650  CImg<T> operator-() const {
10651  return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
10652  }
10653 
10655 
10659  template<typename t>
10660  CImg<_cimg_Tt> operator-(const t value) const {
10661  return CImg<_cimg_Tt>(*this,false)-=value;
10662  }
10663 
10665 
10669  CImg<Tfloat> operator-(const char *const expression) const {
10670  return CImg<Tfloat>(*this,false)-=expression;
10671  }
10672 
10674 
10678  template<typename t>
10679  CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
10680  return CImg<_cimg_Tt>(*this,false)-=img;
10681  }
10682 
10684 
10687  template<typename t>
10688  CImg<T>& operator*=(const t value) {
10689  cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd * value);
10690  return *this;
10691  }
10692 
10694 
10697  CImg<T>& operator*=(const char *const expression) {
10698  const unsigned int omode = cimg::exception_mode();
10699  cimg::exception_mode() = 0;
10700  try {
10701  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10702  _cimg_math_parser mp(base,expression,"operator*=");
10703  T *ptrd = _data;
10704  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd * mp.eval(x,y,z,c)); ++ptrd; }
10705  } catch (CImgException&) {
10706  cimg::exception_mode() = omode;
10707  mul(CImg<T>(_width,_height,_depth,_spectrum,expression,true));
10708  }
10709  cimg::exception_mode() = omode;
10710  return *this;
10711  }
10712 
10714 
10728  template<typename t>
10729  CImg<T>& operator*=(const CImg<t>& img) {
10730  return ((*this)*img).move_to(*this);
10731  }
10732 
10734 
10738  template<typename t>
10739  CImg<_cimg_Tt> operator*(const t value) const {
10740  return CImg<_cimg_Tt>(*this,false)*=value;
10741  }
10742 
10744 
10748  CImg<Tfloat> operator*(const char *const expression) const {
10749  return CImg<Tfloat>(*this,false)*=expression;
10750  }
10751 
10753 
10757  template<typename t>
10758  CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
10759  if (_width!=img._height || _depth!=1 || _spectrum!=1)
10760  throw CImgArgumentException(_cimg_instance
10761  "operator*(): Invalid multiplication of instance by specified matrix (%u,%u,%u,%u,%p)",
10762  cimg_instance,
10763  img._width,img._height,img._depth,img._spectrum,img._data);
10764  CImg<_cimg_Tt> res(img._width,_height);
10765  _cimg_Ttdouble value;
10766 #ifdef cimg_use_openmp
10767 #pragma omp parallel for if (size()>=1000 && img.size()>=1000) private(value)
10768  cimg_forXY(res,i,j) { value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); res(i,j) = (_cimg_Tt)value; }
10769 #else
10770  _cimg_Tt *ptrd = res._data;
10771  cimg_forXY(res,i,j) { value = 0; cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k); *(ptrd++) = (_cimg_Tt)value; }
10772 #endif
10773  return res;
10774  }
10775 
10777 
10780  template<typename t>
10781  CImg<T>& operator/=(const t value) {
10782  cimg_for(*this,ptrd,T) *ptrd = (T)(*ptrd / value);
10783  return *this;
10784  }
10785 
10787 
10790  CImg<T>& operator/=(const char *const expression) {
10791  const unsigned int omode = cimg::exception_mode();
10792  cimg::exception_mode() = 0;
10793  try {
10794  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10795  _cimg_math_parser mp(base,expression,"operator/=");
10796  T *ptrd = _data;
10797  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)(*ptrd / mp.eval(x,y,z,c)); ++ptrd; }
10798  } catch (CImgException&) {
10799  cimg::exception_mode() = omode;
10800  div(CImg<T>(_width,_height,_depth,_spectrum,expression,true));
10801  }
10802  cimg::exception_mode() = omode;
10803  return *this;
10804  }
10805 
10807 
10815  template<typename t>
10816  CImg<T>& operator/=(const CImg<t>& img) {
10817  return (*this*img.get_invert()).move_to(*this);
10818  }
10819 
10821 
10825  template<typename t>
10826  CImg<_cimg_Tt> operator/(const t value) const {
10827  return CImg<_cimg_Tt>(*this,false)/=value;
10828  }
10829 
10831 
10835  CImg<Tfloat> operator/(const char *const expression) const {
10836  return CImg<Tfloat>(*this,false)/=expression;
10837  }
10838 
10840 
10844  template<typename t>
10845  CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
10846  return (*this)*img.get_invert();
10847  }
10848 
10850 
10853  template<typename t>
10854  CImg<T>& operator%=(const t value) {
10855  cimg_for(*this,ptrd,T) *ptrd = (T)cimg::mod(*ptrd,(T)value);
10856  return *this;
10857  }
10858 
10860 
10863  CImg<T>& operator%=(const char *const expression) {
10864  const unsigned int omode = cimg::exception_mode();
10865  cimg::exception_mode() = 0;
10866  try {
10867  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10868  _cimg_math_parser mp(base,expression,"operator%=");
10869  T *ptrd = _data;
10870  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::mod(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
10871  } catch (CImgException&) {
10872  cimg::exception_mode() = omode;
10873  *this%=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10874  }
10875  cimg::exception_mode() = omode;
10876  return *this;
10877  }
10878 
10880 
10883  template<typename t>
10884  CImg<T>& operator%=(const CImg<t>& img) {
10885  const unsigned long siz = size(), isiz = img.size();
10886  if (siz && isiz) {
10887  if (is_overlapped(img)) return *this%=+img;
10888  T *ptrd = _data, *const ptre = _data + siz;
10889  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10890  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
10891  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
10892  }
10893  return *this;
10894  }
10895 
10897 
10901  template<typename t>
10902  CImg<_cimg_Tt> operator%(const t value) const {
10903  return CImg<_cimg_Tt>(*this,false)%=value;
10904  }
10905 
10907 
10911  CImg<Tfloat> operator%(const char *const expression) const {
10912  return CImg<Tfloat>(*this,false)%=expression;
10913  }
10914 
10916 
10920  template<typename t>
10921  CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
10922  return CImg<_cimg_Tt>(*this,false)%=img;
10923  }
10924 
10926 
10929  template<typename t>
10930  CImg<T>& operator&=(const t value) {
10931  cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)value);
10932  return *this;
10933  }
10934 
10936 
10939  CImg<T>& operator&=(const char *const expression) {
10940  const unsigned int omode = cimg::exception_mode();
10941  cimg::exception_mode() = 0;
10942  try {
10943  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
10944  _cimg_math_parser mp(base,expression,"operator&=");
10945  T *ptrd = _data;
10946  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd & (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
10947  } catch (CImgException&) {
10948  cimg::exception_mode() = omode;
10949  *this&=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
10950  }
10951  cimg::exception_mode() = omode;
10952  return *this;
10953  }
10954 
10956 
10959  template<typename t>
10960  CImg<T>& operator&=(const CImg<t>& img) {
10961  const unsigned long siz = size(), isiz = img.size();
10962  if (siz && isiz) {
10963  if (is_overlapped(img)) return *this&=+img;
10964  T *ptrd = _data, *const ptre = _data + siz;
10965  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
10966  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
10967  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd & (unsigned long)*(ptrs++));
10968  }
10969  return *this;
10970  }
10971 
10973 
10977  template<typename t>
10978  CImg<T> operator&(const t value) const {
10979  return (+*this)&=value;
10980  }
10981 
10983 
10987  CImg<T> operator&(const char *const expression) const {
10988  return (+*this)&=expression;
10989  }
10990 
10992 
10996  template<typename t>
10997  CImg<T> operator&(const CImg<t>& img) const {
10998  return (+*this)&=img;
10999  }
11000 
11002 
11005  template<typename t>
11006  CImg<T>& operator|=(const t value) {
11007  cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)value);
11008  return *this;
11009  }
11010 
11012 
11015  CImg<T>& operator|=(const char *const expression) {
11016  const unsigned int omode = cimg::exception_mode();
11017  cimg::exception_mode() = 0;
11018  try {
11019  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11020  _cimg_math_parser mp(base,expression,"operator|=");
11021  T *ptrd = _data;
11022  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd | (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
11023  } catch (CImgException&) {
11024  cimg::exception_mode() = omode;
11025  *this|=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11026  }
11027  cimg::exception_mode() = omode;
11028  return *this;
11029  }
11030 
11032 
11035  template<typename t>
11036  CImg<T>& operator|=(const CImg<t>& img) {
11037  const unsigned long siz = size(), isiz = img.size();
11038  if (siz && isiz) {
11039  if (is_overlapped(img)) return *this|=+img;
11040  T *ptrd = _data, *const ptre = _data + siz;
11041  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11042  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
11043  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd | (unsigned long)*(ptrs++));
11044  }
11045  return *this;
11046  }
11047 
11049 
11053  template<typename t>
11054  CImg<T> operator|(const t value) const {
11055  return (+*this)|=value;
11056  }
11057 
11059 
11063  CImg<T> operator|(const char *const expression) const {
11064  return (+*this)|=expression;
11065  }
11066 
11068 
11072  template<typename t>
11073  CImg<T> operator|(const CImg<t>& img) const {
11074  return (+*this)|=img;
11075  }
11076 
11078 
11083  template<typename t>
11084  CImg<T>& operator^=(const t value) {
11085  cimg_for(*this,ptrd,T) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)value);
11086  return *this;
11087  }
11088 
11090 
11095  CImg<T>& operator^=(const char *const expression) {
11096  const unsigned int omode = cimg::exception_mode();
11097  cimg::exception_mode() = 0;
11098  try {
11099  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11100  _cimg_math_parser mp(base,expression,"operator^=");
11101  T *ptrd = _data;
11102  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)mp.eval(x,y,z,c)); ++ptrd; }
11103  } catch (CImgException&) {
11104  cimg::exception_mode() = omode;
11105  *this^=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11106  }
11107  cimg::exception_mode() = omode;
11108  return *this;
11109  }
11110 
11112 
11117  template<typename t>
11118  CImg<T>& operator^=(const CImg<t>& img) {
11119  const unsigned long siz = size(), isiz = img.size();
11120  if (siz && isiz) {
11121  if (is_overlapped(img)) return *this^=+img;
11122  T *ptrd = _data, *const ptre = _data + siz;
11123  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11124  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
11125  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((unsigned long)*ptrd ^ (unsigned long)*(ptrs++));
11126  }
11127  return *this;
11128  }
11129 
11131 
11135  template<typename t>
11136  CImg<T> operator^(const t value) const {
11137  return (+*this)^=value;
11138  }
11139 
11141 
11145  CImg<T> operator^(const char *const expression) const {
11146  return (+*this)^=expression;
11147  }
11148 
11150 
11154  template<typename t>
11155  CImg<T> operator^(const CImg<t>& img) const {
11156  return (+*this)^=img;
11157  }
11158 
11160 
11163  template<typename t>
11164  CImg<T>& operator<<=(const t value) {
11165  cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) << (int)value);
11166  return *this;
11167  }
11168 
11170 
11173  CImg<T>& operator<<=(const char *const expression) {
11174  const unsigned int omode = cimg::exception_mode();
11175  cimg::exception_mode() = 0;
11176  try {
11177  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11178  _cimg_math_parser mp(base,expression,"operator<<=");
11179  T *ptrd = _data;
11180  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd << (int)mp.eval(x,y,z,c)); ++ptrd; }
11181  } catch (CImgException&) {
11182  cimg::exception_mode() = omode;
11183  *this<<=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11184  }
11185  cimg::exception_mode() = omode;
11186  return *this;
11187  }
11188 
11190 
11193  template<typename t>
11194  CImg<T>& operator<<=(const CImg<t>& img) {
11195  const unsigned long siz = size(), isiz = img.size();
11196  if (siz && isiz) {
11197  if (is_overlapped(img)) return *this^=+img;
11198  T *ptrd = _data, *const ptre = _data + siz;
11199  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11200  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
11201  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd << (int)*(ptrs++));
11202  }
11203  return *this;
11204  }
11205 
11207 
11211  template<typename t>
11212  CImg<T> operator<<(const t value) const {
11213  return (+*this)<<=value;
11214  }
11215 
11217 
11221  CImg<T> operator<<(const char *const expression) const {
11222  return (+*this)<<=expression;
11223  }
11224 
11226 
11230  template<typename t>
11231  CImg<T> operator<<(const CImg<t>& img) const {
11232  return (+*this)<<=img;
11233  }
11234 
11236 
11239  template<typename t>
11240  CImg<T>& operator>>=(const t value) {
11241  cimg_for(*this,ptrd,T) *ptrd = (T)(((long)*ptrd) >> (int)value);
11242  return *this;
11243  }
11244 
11246 
11249  CImg<T>& operator>>=(const char *const expression) {
11250  const unsigned int omode = cimg::exception_mode();
11251  cimg::exception_mode() = 0;
11252  try {
11253  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11254  _cimg_math_parser mp(base,expression,"operator<<=");
11255  T *ptrd = _data;
11256  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)((long)*ptrd >> (int)mp.eval(x,y,z,c)); ++ptrd; }
11257  } catch (CImgException&) {
11258  cimg::exception_mode() = omode;
11259  *this>>=CImg<T>(_width,_height,_depth,_spectrum,expression,true);
11260  }
11261  cimg::exception_mode() = omode;
11262  return *this;
11263  }
11264 
11266 
11269  template<typename t>
11270  CImg<T>& operator>>=(const CImg<t>& img) {
11271  const unsigned long siz = size(), isiz = img.size();
11272  if (siz && isiz) {
11273  if (is_overlapped(img)) return *this^=+img;
11274  T *ptrd = _data, *const ptre = _data + siz;
11275  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
11276  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
11277  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((long)*ptrd >> (int)*(ptrs++));
11278  }
11279  return *this;
11280  }
11281 
11283 
11287  template<typename t>
11288  CImg<T> operator>>(const t value) const {
11289  return (+*this)>>=value;
11290  }
11291 
11293 
11297  CImg<T> operator>>(const char *const expression) const {
11298  return (+*this)>>=expression;
11299  }
11300 
11302 
11306  template<typename t>
11307  CImg<T> operator>>(const CImg<t>& img) const {
11308  return (+*this)>>=img;
11309  }
11310 
11312 
11315  CImg<T> operator~() const {
11316  CImg<T> res(_width,_height,_depth,_spectrum);
11317  const T *ptrs = _data;
11318  cimg_for(res,ptrd,T) { const unsigned long value = (unsigned long)*(ptrs++); *ptrd = (T)~value; }
11319  return res;
11320  }
11321 
11323 
11327  template<typename t>
11328  bool operator==(const t value) const {
11329  if (is_empty()) return false;
11330  typedef _cimg_Tt Tt;
11331  bool is_equal = true;
11332  for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
11333  return is_equal;
11334  }
11335 
11337 
11341  bool operator==(const char *const expression) const {
11342  if (is_empty()) return !*expression;
11343  const unsigned int omode = cimg::exception_mode();
11344  cimg::exception_mode() = 0;
11345  bool is_equal = true;
11346  try {
11347  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
11348  _cimg_math_parser mp(base,expression,"operator<<=");
11349  const T *ptrs = _data;
11350  cimg_forXYZC(*this,x,y,z,c) { if (!is_equal) break; is_equal = ((double)*(ptrs++)==mp.eval(x,y,z,c)); }
11351  } catch (CImgException&) {
11352  cimg::exception_mode() = omode;
11353  is_equal = (*this==CImg<T>(_width,_height,_depth,_spectrum,expression,true));
11354  }
11355  cimg::exception_mode() = omode;
11356  return is_equal;
11357  }
11358 
11360 
11375  template<typename t>
11376  bool operator==(const CImg<t>& img) const {
11377  typedef _cimg_Tt Tt;
11378  const unsigned long siz = size();
11379  bool is_equal = true;
11380  if (siz!=img.size()) return false;
11381  t *ptrs = img._data + siz;
11382  for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
11383  return is_equal;
11384  }
11385 
11387 
11391  template<typename t>
11392  bool operator!=(const t value) const {
11393  return !((*this)==value);
11394  }
11395 
11397 
11401  bool operator!=(const char *const expression) const {
11402  return !((*this)==expression);
11403  }
11404 
11406 
11412  template<typename t>
11413  bool operator!=(const CImg<t>& img) const {
11414  return !((*this)==img);
11415  }
11416 
11418 
11446  template<typename t>
11448  return CImgList<_cimg_Tt>(*this,img);
11449  }
11450 
11452 
11461  template<typename t>
11463  return CImgList<_cimg_Tt>(list,false).insert(*this,0);
11464  }
11465 
11467 
11481  CImgList<T> operator<(const char axis) const {
11482  return get_split(axis);
11483  }
11484 
11486  //-------------------------------------
11487  //
11489 
11490  //-------------------------------------
11491 
11493 
11500  static const char* pixel_type() {
11501  return cimg::type<T>::string();
11502  }
11503 
11505 
11515  int width() const {
11516  return (int)_width;
11517  }
11518 
11520 
11529  int height() const {
11530  return (int)_height;
11531  }
11532 
11534 
11545  int depth() const {
11546  return (int)_depth;
11547  }
11548 
11550 
11563  int spectrum() const {
11564  return (int)_spectrum;
11565  }
11566 
11568 
11582  unsigned long size() const {
11583  return (unsigned long)_width*_height*_depth*_spectrum;
11584  }
11585 
11587 
11596  T* data() {
11597  return _data;
11598  }
11599 
11601  const T* data() const {
11602  return _data;
11603  }
11604 
11606 
11617 #if cimg_verbosity>=3
11618  T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
11619  const unsigned long off = (unsigned long)offset(x,y,z,c);
11620  if (off>=size()) {
11621  cimg::warn(_cimg_instance
11622  "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
11623  cimg_instance,
11624  x,y,z,c,off);
11625  return _data;
11626  }
11627  return _data + off;
11628  }
11629 
11631  const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
11632  return const_cast<CImg<T>*>(this)->data(x,y,z,c);
11633  }
11634 #else
11635  T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
11636  return _data + x + y*(unsigned long)_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth;
11637  }
11638 
11639  const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
11640  return _data + x + y*_width + z*(unsigned long)_width*_height + c*(unsigned long)_width*_height*_depth;
11641  }
11642 #endif
11643 
11645 
11660  long offset(const int x, const int y=0, const int z=0, const int c=0) const {
11661  return x + y*(long)_width + z*(long)_width*_height + c*(long)_width*_height*_depth;
11662  }
11663 
11665 
11671  return _data;
11672  }
11673 
11676  return _data;
11677  }
11678 
11680 
11696  return _data + size();
11697  }
11698 
11701  return _data + size();
11702  }
11703 
11705 
11710  T& front() {
11711  return *_data;
11712  }
11713 
11715  const T& front() const {
11716  return *_data;
11717  }
11718 
11720 
11726  T& back() {
11727  return *(_data + size() - 1);
11728  }
11729 
11731  const T& back() const {
11732  return *(_data + size() - 1);
11733  }
11734 
11736 
11748  T& at(const int offset, const T out_value) {
11749  return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
11750  }
11751 
11753  T at(const int offset, const T out_value) const {
11754  return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
11755  }
11756 
11758 
11771  T& at(const int offset) {
11772  if (is_empty())
11773  throw CImgInstanceException(_cimg_instance
11774  "at(): Empty instance.",
11775  cimg_instance);
11776  return _at(offset);
11777  }
11778 
11779  T& _at(const int offset) {
11780  const unsigned int siz = (unsigned int)size();
11781  return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset];
11782  }
11783 
11785  T at(const int offset) const {
11786  if (is_empty())
11787  throw CImgInstanceException(_cimg_instance
11788  "at(): Empty instance.",
11789  cimg_instance);
11790  return _at(offset);
11791  }
11792 
11793  T _at(const int offset) const {
11794  const unsigned int siz = (unsigned int)size();
11795  return (*this)[offset<0?0:(unsigned int)offset>=siz?siz-1:offset];
11796  }
11797 
11799 
11814  T& atX(const int x, const int y, const int z, const int c, const T out_value) {
11815  return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11816  }
11817 
11819  T atX(const int x, const int y, const int z, const int c, const T out_value) const {
11820  return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
11821  }
11822 
11824 
11840  T& atX(const int x, const int y=0, const int z=0, const int c=0) {
11841  if (is_empty())
11842  throw CImgInstanceException(_cimg_instance
11843  "atX(): Empty instance.",
11844  cimg_instance);
11845  return _atX(x,y,z,c);
11846  }
11847 
11848  T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
11849  return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
11850  }
11851 
11853  T atX(const int x, const int y=0, const int z=0, const int c=0) const {
11854  if (is_empty())
11855  throw CImgInstanceException(_cimg_instance
11856  "atX(): Empty instance.",
11857  cimg_instance);
11858  return _atX(x,y,z,c);
11859  }
11860 
11861  T _atX(const int x, const int y=0, const int z=0, const int c=0) const {
11862  return (*this)(x<0?0:(x>=width()?width()-1:x),y,z,c);
11863  }
11864 
11866 
11869  T& atXY(const int x, const int y, const int z, const int c, const T out_value) {
11870  return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11871  }
11872 
11874  T atXY(const int x, const int y, const int z, const int c, const T out_value) const {
11875  return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
11876  }
11877 
11879 
11884  T& atXY(const int x, const int y, const int z=0, const int c=0) {
11885  if (is_empty())
11886  throw CImgInstanceException(_cimg_instance
11887  "atXY(): Empty instance.",
11888  cimg_instance);
11889  return _atXY(x,y,z,c);
11890  }
11891 
11892  T& _atXY(const int x, const int y, const int z=0, const int c=0) {
11893  return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
11894  }
11895 
11897  T atXY(const int x, const int y, const int z=0, const int c=0) const {
11898  if (is_empty())
11899  throw CImgInstanceException(_cimg_instance
11900  "atXY(): Empty instance.",
11901  cimg_instance);
11902  return _atXY(x,y,z,c);
11903  }
11904 
11905  T _atXY(const int x, const int y, const int z=0, const int c=0) const {
11906  return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),z,c);
11907  }
11908 
11910 
11913  T& atXYZ(const int x, const int y, const int z, const int c, const T out_value) {
11914  return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
11915  (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11916  }
11917 
11919  T atXYZ(const int x, const int y, const int z, const int c, const T out_value) const {
11920  return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
11921  }
11922 
11924 
11929  T& atXYZ(const int x, const int y, const int z, const int c=0) {
11930  if (is_empty())
11931  throw CImgInstanceException(_cimg_instance
11932  "atXYZ(): Empty instance.",
11933  cimg_instance);
11934  return _atXYZ(x,y,z,c);
11935  }
11936 
11937  T& _atXYZ(const int x, const int y, const int z, const int c=0) {
11938  return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
11939  z<0?0:(z>=depth()?depth()-1:z),c);
11940  }
11941 
11943  T atXYZ(const int x, const int y, const int z, const int c=0) const {
11944  if (is_empty())
11945  throw CImgInstanceException(_cimg_instance
11946  "atXYZ(): Empty instance.",
11947  cimg_instance);
11948  return _atXYZ(x,y,z,c);
11949  }
11950 
11951  T _atXYZ(const int x, const int y, const int z, const int c=0) const {
11952  return (*this)(x<0?0:(x>=width()?width()-1:x),y<0?0:(y>=height()?height()-1:y),
11953  z<0?0:(z>=depth()?depth()-1:z),c);
11954  }
11955 
11957 
11960  T& atXYZC(const int x, const int y, const int z, const int c, const T out_value) {
11961  return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
11962  (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
11963  }
11964 
11966  T atXYZC(const int x, const int y, const int z, const int c, const T out_value) const {
11967  return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:(*this)(x,y,z,c);
11968  }
11969 
11971 
11976  T& atXYZC(const int x, const int y, const int z, const int c) {
11977  if (is_empty())
11978  throw CImgInstanceException(_cimg_instance
11979  "atXYZC(): Empty instance.",
11980  cimg_instance);
11981  return _atXYZC(x,y,z,c);
11982  }
11983 
11984  T& _atXYZC(const int x, const int y, const int z, const int c) {
11985  return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
11986  z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
11987  }
11988 
11990  T atXYZC(const int x, const int y, const int z, const int c) const {
11991  if (is_empty())
11992  throw CImgInstanceException(_cimg_instance
11993  "atXYZC(): Empty instance.",
11994  cimg_instance);
11995  return _atXYZC(x,y,z,c);
11996  }
11997 
11998  T _atXYZC(const int x, const int y, const int z, const int c) const {
11999  return (*this)(x<0?0:(x>=width()?width()-1:x), y<0?0:(y>=height()?height()-1:y),
12000  z<0?0:(z>=depth()?depth()-1:z), c<0?0:(c>=spectrum()?spectrum()-1:c));
12001  }
12002 
12004 
12019  Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T out_value) const {
12020  const int
12021  x = (int)fx - (fx>=0?0:1), nx = x + 1;
12022  const float
12023  dx = fx - x;
12024  const Tfloat
12025  Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
12026  return Ic + dx*(In-Ic);
12027  }
12028 
12030 
12044  Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12045  if (is_empty())
12046  throw CImgInstanceException(_cimg_instance
12047  "linear_atX(): Empty instance.",
12048  cimg_instance);
12049 
12050  return _linear_atX(fx,y,z,c);
12051  }
12052 
12053  Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12054  const float
12055  nfx = fx<0?0:(fx>_width-1?_width-1:fx);
12056  const unsigned int
12057  x = (unsigned int)nfx;
12058  const float
12059  dx = nfx - x;
12060  const unsigned int
12061  nx = dx>0?x+1:x;
12062  const Tfloat
12063  Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
12064  return Ic + dx*(In-Ic);
12065  }
12066 
12068 
12072  Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const {
12073  const int
12074  x = (int)fx - (fx>=0?0:1), nx = x + 1,
12075  y = (int)fy - (fy>=0?0:1), ny = y + 1;
12076  const float
12077  dx = fx - x,
12078  dy = fy - y;
12079  const Tfloat
12080  Icc = (Tfloat)atXY(x,y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value),
12081  Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
12082  return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
12083  }
12084 
12086 
12092  Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12093  if (is_empty())
12094  throw CImgInstanceException(_cimg_instance
12095  "linear_atXY(): Empty instance.",
12096  cimg_instance);
12097 
12098  return _linear_atXY(fx,fy,z,c);
12099  }
12100 
12101  Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12102  const float
12103  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12104  nfy = fy<0?0:(fy>_height-1?_height-1:fy);
12105  const unsigned int
12106  x = (unsigned int)nfx,
12107  y = (unsigned int)nfy;
12108  const float
12109  dx = nfx - x,
12110  dy = nfy - y;
12111  const unsigned int
12112  nx = dx>0?x+1:x,
12113  ny = dy>0?y+1:y;
12114  const Tfloat
12115  Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
12116  Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
12117  return Icc + dx*(Inc-Icc + dy*(Icc+Inn-Icn-Inc)) + dy*(Icn-Icc);
12118  }
12119 
12121 
12125  Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const {
12126  const int
12127  x = (int)fx - (fx>=0?0:1), nx = x + 1,
12128  y = (int)fy - (fy>=0?0:1), ny = y + 1,
12129  z = (int)fz - (fz>=0?0:1), nz = z + 1;
12130  const float
12131  dx = fx - x,
12132  dy = fy - y,
12133  dz = fz - z;
12134  const Tfloat
12135  Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
12136  Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
12137  Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
12138  Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
12139  return Iccc +
12140  dx*(Incc-Iccc +
12141  dy*(Iccc+Innc-Icnc-Incc +
12142  dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
12143  dz*(Iccc+Incn-Iccn-Incc)) +
12144  dy*(Icnc-Iccc +
12145  dz*(Iccc+Icnn-Iccn-Icnc)) +
12146  dz*(Iccn-Iccc);
12147  }
12148 
12150 
12156  Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
12157  if (is_empty())
12158  throw CImgInstanceException(_cimg_instance
12159  "linear_atXYZ(): Empty instance.",
12160  cimg_instance);
12161 
12162  return _linear_atXYZ(fx,fy,fz,c);
12163  }
12164 
12165  Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
12166  const float
12167  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12168  nfy = fy<0?0:(fy>_height-1?_height-1:fy),
12169  nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
12170  const unsigned int
12171  x = (unsigned int)nfx,
12172  y = (unsigned int)nfy,
12173  z = (unsigned int)nfz;
12174  const float
12175  dx = nfx - x,
12176  dy = nfy - y,
12177  dz = nfz - z;
12178  const unsigned int
12179  nx = dx>0?x+1:x,
12180  ny = dy>0?y+1:y,
12181  nz = dz>0?z+1:z;
12182  const Tfloat
12183  Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
12184  Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
12185  Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
12186  Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
12187  return Iccc +
12188  dx*(Incc-Iccc +
12189  dy*(Iccc+Innc-Icnc-Incc +
12190  dz*(Iccn+Innn+Icnc+Incc-Icnn-Incn-Iccc-Innc)) +
12191  dz*(Iccc+Incn-Iccn-Incc)) +
12192  dy*(Icnc-Iccc +
12193  dz*(Iccc+Icnn-Iccn-Icnc)) +
12194  dz*(Iccn-Iccc);
12195  }
12196 
12198 
12202  Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T out_value) const {
12203  const int
12204  x = (int)fx - (fx>=0?0:1), nx = x + 1,
12205  y = (int)fy - (fy>=0?0:1), ny = y + 1,
12206  z = (int)fz - (fz>=0?0:1), nz = z + 1,
12207  c = (int)fc - (fc>=0?0:1), nc = c + 1;
12208  const float
12209  dx = fx - x,
12210  dy = fy - y,
12211  dz = fz - z,
12212  dc = fc - c;
12213  const Tfloat
12214  Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
12215  Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
12216  Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
12217  Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
12218  Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
12219  Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
12220  Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
12221  Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
12222  return Icccc +
12223  dx*(Inccc-Icccc +
12224  dy*(Icccc+Inncc-Icncc-Inccc +
12225  dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
12226  dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
12227  dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
12228  dz*(Icccc+Incnc-Iccnc-Inccc +
12229  dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
12230  dc*(Icccc+Inccn-Inccc-Icccn)) +
12231  dy*(Icncc-Icccc +
12232  dz*(Icccc+Icnnc-Iccnc-Icncc +
12233  dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
12234  dc*(Icccc+Icncn-Icncc-Icccn)) +
12235  dz*(Iccnc-Icccc +
12236  dc*(Icccc+Iccnn-Iccnc-Icccn)) +
12237  dc*(Icccn-Icccc);
12238  }
12239 
12241 
12247  Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
12248  if (is_empty())
12249  throw CImgInstanceException(_cimg_instance
12250  "linear_atXYZC(): Empty instance.",
12251  cimg_instance);
12252 
12253  return _linear_atXYZC(fx,fy,fz,fc);
12254  }
12255 
12256  Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
12257  const float
12258  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12259  nfy = fy<0?0:(fy>_height-1?_height-1:fy),
12260  nfz = fz<0?0:(fz>_depth-1?_depth-1:fz),
12261  nfc = fc<0?0:(fc>_spectrum-1?_spectrum-1:fc);
12262  const unsigned int
12263  x = (unsigned int)nfx,
12264  y = (unsigned int)nfy,
12265  z = (unsigned int)nfz,
12266  c = (unsigned int)nfc;
12267  const float
12268  dx = nfx - x,
12269  dy = nfy - y,
12270  dz = nfz - z,
12271  dc = nfc - c;
12272  const unsigned int
12273  nx = dx>0?x+1:x,
12274  ny = dy>0?y+1:y,
12275  nz = dz>0?z+1:z,
12276  nc = dc>0?c+1:c;
12277  const Tfloat
12278  Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
12279  Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
12280  Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
12281  Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
12282  Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
12283  Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
12284  Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
12285  Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
12286  return Icccc +
12287  dx*(Inccc-Icccc +
12288  dy*(Icccc+Inncc-Icncc-Inccc +
12289  dz*(Iccnc+Innnc+Icncc+Inccc-Icnnc-Incnc-Icccc-Inncc +
12290  dc*(Iccnn+Innnn+Icncn+Inccn+Icnnc+Incnc+Icccc+Inncc-Icnnn-Incnn-Icccn-Inncn-Iccnc-Innnc-Icncc-Inccc)) +
12291  dc*(Icccn+Inncn+Icncc+Inccc-Icncn-Inccn-Icccc-Inncc)) +
12292  dz*(Icccc+Incnc-Iccnc-Inccc +
12293  dc*(Icccn+Incnn+Iccnc+Inccc-Iccnn-Inccn-Icccc-Incnc)) +
12294  dc*(Icccc+Inccn-Inccc-Icccn)) +
12295  dy*(Icncc-Icccc +
12296  dz*(Icccc+Icnnc-Iccnc-Icncc +
12297  dc*(Icccn+Icnnn+Iccnc+Icncc-Iccnn-Icncn-Icccc-Icnnc)) +
12298  dc*(Icccc+Icncn-Icncc-Icccn)) +
12299  dz*(Iccnc-Icccc +
12300  dc*(Icccc+Iccnn-Iccnc-Icccn)) +
12301  dc*(Icccn-Icccc);
12302  }
12303 
12305 
12320  Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value) const {
12321  const int
12322  x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
12323  const float
12324  dx = fx - x;
12325  const Tfloat
12326  Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
12327  In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
12328  return Ic + 0.5f*(dx*(-Ip+In) + dx*dx*(2*Ip-5*Ic+4*In-Ia) + dx*dx*dx*(-Ip+3*Ic-3*In+Ia));
12329  }
12330 
12332 
12335  Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T out_value,
12336  const Tfloat min_value, const Tfloat max_value) const {
12337  const Tfloat val = cubic_atX(fx,y,z,c,out_value);
12338  return val<min_value?min_value:val>max_value?max_value:val;
12339  }
12340 
12342 
12355  Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12356  if (is_empty())
12357  throw CImgInstanceException(_cimg_instance
12358  "cubic_atX(): Empty instance.",
12359  cimg_instance);
12360  return _cubic_atX(fx,y,z,c);
12361  }
12362 
12363  Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
12364  const float
12365  nfx = fx<0?0:(fx>_width-1?_width-1:fx);
12366  const int
12367  x = (int)nfx;
12368  const float
12369  dx = nfx - x;
12370  const int
12371  px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2;
12372  const Tfloat
12373  Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
12374  In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
12375  return Ic + 0.5f*(dx*(-Ip+In) + dx*dx*(2*Ip-5*Ic+4*In-Ia) + dx*dx*dx*(-Ip+3*Ic-3*In+Ia));
12376  }
12377 
12379 
12382  Tfloat cubic_atX(const float fx, const int y, const int z, const int c,
12383  const Tfloat min_value, const Tfloat max_value) const {
12384  const Tfloat val = cubic_atX(fx,y,z,c);
12385  return val<min_value?min_value:val>max_value?max_value:val;
12386  }
12387 
12388  Tfloat _cubic_atX(const float fx, const int y, const int z, const int c,
12389  const Tfloat min_value, const Tfloat max_value) const {
12390  const Tfloat val = _cubic_atX(fx,y,z,c);
12391  return val<min_value?min_value:val>max_value?max_value:val;
12392  }
12393 
12395 
12399  Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value) const {
12400  const int
12401  x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
12402  y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
12403  const float dx = fx - x, dy = fy - y;
12404  const Tfloat
12405  Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value), Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
12406  Ip = Icp + 0.5f*(dx*(-Ipp+Inp) + dx*dx*(2*Ipp-5*Icp+4*Inp-Iap) + dx*dx*dx*(-Ipp+3*Icp-3*Inp+Iap)),
12407  Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value),
12408  Ic = Icc + 0.5f*(dx*(-Ipc+Inc) + dx*dx*(2*Ipc-5*Icc+4*Inc-Iac) + dx*dx*dx*(-Ipc+3*Icc-3*Inc+Iac)),
12409  Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
12410  In = Icn + 0.5f*(dx*(-Ipn+Inn) + dx*dx*(2*Ipn-5*Icn+4*Inn-Ian) + dx*dx*dx*(-Ipn+3*Icn-3*Inn+Ian)),
12411  Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value), Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
12412  Ia = Ica + 0.5f*(dx*(-Ipa+Ina) + dx*dx*(2*Ipa-5*Ica+4*Ina-Iaa) + dx*dx*dx*(-Ipa+3*Ica-3*Ina+Iaa));
12413  return Ic + 0.5f*(dy*(-Ip+In) + dy*dy*(2*Ip-5*Ic+4*In-Ia) + dy*dy*dy*(-Ip+3*Ic-3*In+Ia));
12414  }
12415 
12417 
12420  Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T out_value,
12421  const Tfloat min_value, const Tfloat max_value) const {
12422  const Tfloat val = cubic_atXY(fx,fy,z,c,out_value);
12423  return val<min_value?min_value:val>max_value?max_value:val;
12424  }
12425 
12427 
12433  Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12434  if (is_empty())
12435  throw CImgInstanceException(_cimg_instance
12436  "cubic_atXY(): Empty instance.",
12437  cimg_instance);
12438  return _cubic_atXY(fx,fy,z,c);
12439  }
12440 
12441  Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
12442  const float
12443  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12444  nfy = fy<0?0:(fy>_height-1?_height-1:fy);
12445  const int x = (int)nfx, y = (int)nfy;
12446  const float dx = nfx - x, dy = nfy - y;
12447  const int
12448  px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
12449  py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2;
12450  const Tfloat
12451  Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c), Iap = (Tfloat)(*this)(ax,py,z,c),
12452  Ip = Icp + 0.5f*(dx*(-Ipp+Inp) + dx*dx*(2*Ipp-5*Icp+4*Inp-Iap) + dx*dx*dx*(-Ipp+3*Icp-3*Inp+Iap)),
12453  Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c), Iac = (Tfloat)(*this)(ax,y,z,c),
12454  Ic = Icc + 0.5f*(dx*(-Ipc+Inc) + dx*dx*(2*Ipc-5*Icc+4*Inc-Iac) + dx*dx*dx*(-Ipc+3*Icc-3*Inc+Iac)),
12455  Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c), Ian = (Tfloat)(*this)(ax,ny,z,c),
12456  In = Icn + 0.5f*(dx*(-Ipn+Inn) + dx*dx*(2*Ipn-5*Icn+4*Inn-Ian) + dx*dx*dx*(-Ipn+3*Icn-3*Inn+Ian)),
12457  Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c), Iaa = (Tfloat)(*this)(ax,ay,z,c),
12458  Ia = Ica + 0.5f*(dx*(-Ipa+Ina) + dx*dx*(2*Ipa-5*Ica+4*Ina-Iaa) + dx*dx*dx*(-Ipa+3*Ica-3*Ina+Iaa));
12459  return Ic + 0.5f*(dy*(-Ip+In) + dy*dy*(2*Ip-5*Ic+4*In-Ia) + dy*dy*dy*(-Ip+3*Ic-3*In+Ia));
12460  }
12461 
12463 
12466  Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c,
12467  const Tfloat min_value, const Tfloat max_value) const {
12468  const Tfloat val = cubic_atXY(fx,fy,z,c);
12469  return val<min_value?min_value:val>max_value?max_value:val;
12470  }
12471 
12472  Tfloat _cubic_atXY(const float fx, const float fy, const int z, const int c,
12473  const Tfloat min_value, const Tfloat max_value) const {
12474  const Tfloat val = _cubic_atXY(fx,fy,z,c);
12475  return val<min_value?min_value:val>max_value?max_value:val;
12476  }
12477 
12479 
12483  Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value) const {
12484  const int
12485  x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
12486  y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
12487  z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
12488  const float dx = fx - x, dy = fy - y, dz = fz - z;
12489  const Tfloat
12490  Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
12491  Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
12492  Ipp = Icpp + 0.5f*(dx*(-Ippp+Inpp) + dx*dx*(2*Ippp-5*Icpp+4*Inpp-Iapp) + dx*dx*dx*(-Ippp+3*Icpp-3*Inpp+Iapp)),
12493  Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
12494  Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
12495  Icp = Iccp + 0.5f*(dx*(-Ipcp+Incp) + dx*dx*(2*Ipcp-5*Iccp+4*Incp-Iacp) + dx*dx*dx*(-Ipcp+3*Iccp-3*Incp+Iacp)),
12496  Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
12497  Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
12498  Inp = Icnp + 0.5f*(dx*(-Ipnp+Innp) + dx*dx*(2*Ipnp-5*Icnp+4*Innp-Ianp) + dx*dx*dx*(-Ipnp+3*Icnp-3*Innp+Ianp)),
12499  Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
12500  Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
12501  Iap = Icap + 0.5f*(dx*(-Ipap+Inap) + dx*dx*(2*Ipap-5*Icap+4*Inap-Iaap) + dx*dx*dx*(-Ipap+3*Icap-3*Inap+Iaap)),
12502  Ip = Icp + 0.5f*(dy*(-Ipp+Inp) + dy*dy*(2*Ipp-5*Icp+4*Inp-Iap) + dy*dy*dy*(-Ipp+3*Icp-3*Inp+Iap)),
12503  Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
12504  Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
12505  Ipc = Icpc + 0.5f*(dx*(-Ippc+Inpc) + dx*dx*(2*Ippc-5*Icpc+4*Inpc-Iapc) + dx*dx*dx*(-Ippc+3*Icpc-3*Inpc+Iapc)),
12506  Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
12507  Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
12508  Icc = Iccc + 0.5f*(dx*(-Ipcc+Incc) + dx*dx*(2*Ipcc-5*Iccc+4*Incc-Iacc) + dx*dx*dx*(-Ipcc+3*Iccc-3*Incc+Iacc)),
12509  Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
12510  Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
12511  Inc = Icnc + 0.5f*(dx*(-Ipnc+Innc) + dx*dx*(2*Ipnc-5*Icnc+4*Innc-Ianc) + dx*dx*dx*(-Ipnc+3*Icnc-3*Innc+Ianc)),
12512  Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
12513  Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
12514  Iac = Icac + 0.5f*(dx*(-Ipac+Inac) + dx*dx*(2*Ipac-5*Icac+4*Inac-Iaac) + dx*dx*dx*(-Ipac+3*Icac-3*Inac+Iaac)),
12515  Ic = Icc + 0.5f*(dy*(-Ipc+Inc) + dy*dy*(2*Ipc-5*Icc+4*Inc-Iac) + dy*dy*dy*(-Ipc+3*Icc-3*Inc+Iac)),
12516  Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
12517  Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
12518  Ipn = Icpn + 0.5f*(dx*(-Ippn+Inpn) + dx*dx*(2*Ippn-5*Icpn+4*Inpn-Iapn) + dx*dx*dx*(-Ippn+3*Icpn-3*Inpn+Iapn)),
12519  Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
12520  Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
12521  Icn = Iccn + 0.5f*(dx*(-Ipcn+Incn) + dx*dx*(2*Ipcn-5*Iccn+4*Incn-Iacn) + dx*dx*dx*(-Ipcn+3*Iccn-3*Incn+Iacn)),
12522  Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
12523  Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
12524  Inn = Icnn + 0.5f*(dx*(-Ipnn+Innn) + dx*dx*(2*Ipnn-5*Icnn+4*Innn-Iann) + dx*dx*dx*(-Ipnn+3*Icnn-3*Innn+Iann)),
12525  Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
12526  Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
12527  Ian = Ican + 0.5f*(dx*(-Ipan+Inan) + dx*dx*(2*Ipan-5*Ican+4*Inan-Iaan) + dx*dx*dx*(-Ipan+3*Ican-3*Inan+Iaan)),
12528  In = Icn + 0.5f*(dy*(-Ipn+Inn) + dy*dy*(2*Ipn-5*Icn+4*Inn-Ian) + dy*dy*dy*(-Ipn+3*Icn-3*Inn+Ian)),
12529  Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
12530  Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
12531  Ipa = Icpa + 0.5f*(dx*(-Ippa+Inpa) + dx*dx*(2*Ippa-5*Icpa+4*Inpa-Iapa) + dx*dx*dx*(-Ippa+3*Icpa-3*Inpa+Iapa)),
12532  Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
12533  Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
12534  Ica = Icca + 0.5f*(dx*(-Ipca+Inca) + dx*dx*(2*Ipca-5*Icca+4*Inca-Iaca) + dx*dx*dx*(-Ipca+3*Icca-3*Inca+Iaca)),
12535  Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
12536  Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
12537  Ina = Icna + 0.5f*(dx*(-Ipna+Inna) + dx*dx*(2*Ipna-5*Icna+4*Inna-Iana) + dx*dx*dx*(-Ipna+3*Icna-3*Inna+Iana)),
12538  Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
12539  Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
12540  Iaa = Icaa + 0.5f*(dx*(-Ipaa+Inaa) + dx*dx*(2*Ipaa-5*Icaa+4*Inaa-Iaaa) + dx*dx*dx*(-Ipaa+3*Icaa-3*Inaa+Iaaa)),
12541  Ia = Ica + 0.5f*(dy*(-Ipa+Ina) + dy*dy*(2*Ipa-5*Ica+4*Ina-Iaa) + dy*dy*dy*(-Ipa+3*Ica-3*Ina+Iaa));
12542  return Ic + 0.5f*(dz*(-Ip+In) + dz*dz*(2*Ip-5*Ic+4*In-Ia) + dz*dz*dz*(-Ip+3*Ic-3*In+Ia));
12543  }
12544 
12546 
12549  Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T out_value,
12550  const Tfloat min_value, const Tfloat max_value) const {
12551  const Tfloat val = cubic_atXYZ(fx,fy,fz,c,out_value);
12552  return val<min_value?min_value:val>max_value?max_value:val;
12553  }
12554 
12556 
12562  Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
12563  if (is_empty())
12564  throw CImgInstanceException(_cimg_instance
12565  "cubic_atXYZ(): Empty instance.",
12566  cimg_instance);
12567  return _cubic_atXYZ(fx,fy,fz,c);
12568  }
12569 
12570  Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
12571  const float
12572  nfx = fx<0?0:(fx>_width-1?_width-1:fx),
12573  nfy = fy<0?0:(fy>_height-1?_height-1:fy),
12574  nfz = fz<0?0:(fz>_depth-1?_depth-1:fz);
12575  const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
12576  const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
12577  const int
12578  px = x-1<0?0:x-1, nx = dx>0?x+1:x, ax = x+2>=width()?width()-1:x+2,
12579  py = y-1<0?0:y-1, ny = dy>0?y+1:y, ay = y+2>=height()?height()-1:y+2,
12580  pz = z-1<0?0:z-1, nz = dz>0?z+1:z, az = z+2>=depth()?depth()-1:z+2;
12581  const Tfloat
12582  Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
12583  Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
12584  Ipp = Icpp + 0.5f*(dx*(-Ippp+Inpp) + dx*dx*(2*Ippp-5*Icpp+4*Inpp-Iapp) + dx*dx*dx*(-Ippp+3*Icpp-3*Inpp+Iapp)),
12585  Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c),
12586  Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c),
12587  Icp = Iccp + 0.5f*(dx*(-Ipcp+Incp) + dx*dx*(2*Ipcp-5*Iccp+4*Incp-Iacp) + dx*dx*dx*(-Ipcp+3*Iccp-3*Incp+Iacp)),
12588  Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
12589  Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
12590  Inp = Icnp + 0.5f*(dx*(-Ipnp+Innp) + dx*dx*(2*Ipnp-5*Icnp+4*Innp-Ianp) + dx*dx*dx*(-Ipnp+3*Icnp-3*Innp+Ianp)),
12591  Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
12592  Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
12593  Iap = Icap + 0.5f*(dx*(-Ipap+Inap) + dx*dx*(2*Ipap-5*Icap+4*Inap-Iaap) + dx*dx*dx*(-Ipap+3*Icap-3*Inap+Iaap)),
12594  Ip = Icp + 0.5f*(dy*(-Ipp+Inp) + dy*dy*(2*Ipp-5*Icp+4*Inp-Iap) + dy*dy*dy*(-Ipp+3*Icp-3*Inp+Iap)),
12595  Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
12596  Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
12597  Ipc = Icpc + 0.5f*(dx*(-Ippc+Inpc) + dx*dx*(2*Ippc-5*Icpc+4*Inpc-Iapc) + dx*dx*dx*(-Ippc+3*Icpc-3*Inpc+Iapc)),
12598  Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c),
12599  Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c),
12600  Icc = Iccc + 0.5f*(dx*(-Ipcc+Incc) + dx*dx*(2*Ipcc-5*Iccc+4*Incc-Iacc) + dx*dx*dx*(-Ipcc+3*Iccc-3*Incc+Iacc)),
12601  Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
12602  Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
12603  Inc = Icnc + 0.5f*(dx*(-Ipnc+Innc) + dx*dx*(2*Ipnc-5*Icnc+4*Innc-Ianc) + dx*dx*dx*(-Ipnc+3*Icnc-3*Innc+Ianc)),
12604  Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
12605  Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
12606  Iac = Icac + 0.5f*(dx*(-Ipac+Inac) + dx*dx*(2*Ipac-5*Icac+4*Inac-Iaac) + dx*dx*dx*(-Ipac+3*Icac-3*Inac+Iaac)),
12607  Ic = Icc + 0.5f*(dy*(-Ipc+Inc) + dy*dy*(2*Ipc-5*Icc+4*Inc-Iac) + dy*dy*dy*(-Ipc+3*Icc-3*Inc+Iac)),
12608  Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
12609  Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
12610  Ipn = Icpn + 0.5f*(dx*(-Ippn+Inpn) + dx*dx*(2*Ippn-5*Icpn+4*Inpn-Iapn) + dx*dx*dx*(-Ippn+3*Icpn-3*Inpn+Iapn)),
12611  Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c),
12612  Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c),
12613  Icn = Iccn + 0.5f*(dx*(-Ipcn+Incn) + dx*dx*(2*Ipcn-5*Iccn+4*Incn-Iacn) + dx*dx*dx*(-Ipcn+3*Iccn-3*Incn+Iacn)),
12614  Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
12615  Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
12616  Inn = Icnn + 0.5f*(dx*(-Ipnn+Innn) + dx*dx*(2*Ipnn-5*Icnn+4*Innn-Iann) + dx*dx*dx*(-Ipnn+3*Icnn-3*Innn+Iann)),
12617  Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
12618  Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
12619  Ian = Ican + 0.5f*(dx*(-Ipan+Inan) + dx*dx*(2*Ipan-5*Ican+4*Inan-Iaan) + dx*dx*dx*(-Ipan+3*Ican-3*Inan+Iaan)),
12620  In = Icn + 0.5f*(dy*(-Ipn+Inn) + dy*dy*(2*Ipn-5*Icn+4*Inn-Ian) + dy*dy*dy*(-Ipn+3*Icn-3*Inn+Ian)),
12621  Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
12622  Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
12623  Ipa = Icpa + 0.5f*(dx*(-Ippa+Inpa) + dx*dx*(2*Ippa-5*Icpa+4*Inpa-Iapa) + dx*dx*dx*(-Ippa+3*Icpa-3*Inpa+Iapa)),
12624  Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c),
12625  Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c),
12626  Ica = Icca + 0.5f*(dx*(-Ipca+Inca) + dx*dx*(2*Ipca-5*Icca+4*Inca-Iaca) + dx*dx*dx*(-Ipca+3*Icca-3*Inca+Iaca)),
12627  Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
12628  Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
12629  Ina = Icna + 0.5f*(dx*(-Ipna+Inna) + dx*dx*(2*Ipna-5*Icna+4*Inna-Iana) + dx*dx*dx*(-Ipna+3*Icna-3*Inna+Iana)),
12630  Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
12631  Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
12632  Iaa = Icaa + 0.5f*(dx*(-Ipaa+Inaa) + dx*dx*(2*Ipaa-5*Icaa+4*Inaa-Iaaa) + dx*dx*dx*(-Ipaa+3*Icaa-3*Inaa+Iaaa)),
12633  Ia = Ica + 0.5f*(dy*(-Ipa+Ina) + dy*dy*(2*Ipa-5*Ica+4*Ina-Iaa) + dy*dy*dy*(-Ipa+3*Ica-3*Ina+Iaa));
12634  return Ic + 0.5f*(dz*(-Ip+In) + dz*dz*(2*Ip-5*Ic+4*In-Ia) + dz*dz*dz*(-Ip+3*Ic-3*In+Ia));
12635  }
12636 
12638 
12641  Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
12642  const Tfloat min_value, const Tfloat max_value) const {
12643  const Tfloat val = cubic_atXYZ(fx,fy,fz,c);
12644  return val<min_value?min_value:val>max_value?max_value:val;
12645  }
12646 
12647  Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c,
12648  const Tfloat min_value, const Tfloat max_value) const {
12649  const Tfloat val = _cubic_atXYZ(fx,fy,fz,c);
12650  return val<min_value?min_value:val>max_value?max_value:val;
12651  }
12652 
12654 
12667  CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
12668  const bool is_added=false) {
12669  const int
12670  x = (int)fx - (fx>=0?0:1), nx = x + 1,
12671  y = (int)fy - (fy>=0?0:1), ny = y + 1;
12672  const float
12673  dx = fx - x,
12674  dy = fy - y;
12675  if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
12676  if (y>=0 && y<height()) {
12677  if (x>=0 && x<width()) {
12678  const float w1 = (1-dx)*(1-dy), w2 = is_added?1:(1-w1);
12679  (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
12680  }
12681  if (nx>=0 && nx<width()) {
12682  const float w1 = dx*(1-dy), w2 = is_added?1:(1-w1);
12683  (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
12684  }
12685  }
12686  if (ny>=0 && ny<height()) {
12687  if (x>=0 && x<width()) {
12688  const float w1 = (1-dx)*dy, w2 = is_added?1:(1-w1);
12689  (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
12690  }
12691  if (nx>=0 && nx<width()) {
12692  const float w1 = dx*dy, w2 = is_added?1:(1-w1);
12693  (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
12694  }
12695  }
12696  }
12697  return *this;
12698  }
12699 
12701 
12705  CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
12706  const bool is_added=false) {
12707  const int
12708  x = (int)fx - (fx>=0?0:1), nx = x + 1,
12709  y = (int)fy - (fy>=0?0:1), ny = y + 1,
12710  z = (int)fz - (fz>=0?0:1), nz = z + 1;
12711  const float
12712  dx = fx - x,
12713  dy = fy - y,
12714  dz = fz - z;
12715  if (c>=0 && c<spectrum()) {
12716  if (z>=0 && z<depth()) {
12717  if (y>=0 && y<height()) {
12718  if (x>=0 && x<width()) {
12719  const float w1 = (1-dx)*(1-dy)*(1-dz), w2 = is_added?1:(1-w1);
12720  (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
12721  }
12722  if (nx>=0 && nx<width()) {
12723  const float w1 = dx*(1-dy)*(1-dz), w2 = is_added?1:(1-w1);
12724  (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
12725  }
12726  }
12727  if (ny>=0 && ny<height()) {
12728  if (x>=0 && x<width()) {
12729  const float w1 = (1-dx)*dy*(1-dz), w2 = is_added?1:(1-w1);
12730  (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
12731  }
12732  if (nx>=0 && nx<width()) {
12733  const float w1 = dx*dy*(1-dz), w2 = is_added?1:(1-w1);
12734  (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
12735  }
12736  }
12737  }
12738  if (nz>=0 && nz<depth()) {
12739  if (y>=0 && y<height()) {
12740  if (x>=0 && x<width()) {
12741  const float w1 = (1-dx)*(1-dy)*dz, w2 = is_added?1:(1-w1);
12742  (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
12743  }
12744  if (nx>=0 && nx<width()) {
12745  const float w1 = dx*(1-dy)*dz, w2 = is_added?1:(1-w1);
12746  (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
12747  }
12748  }
12749  if (ny>=0 && ny<height()) {
12750  if (x>=0 && x<width()) {
12751  const float w1 = (1-dx)*dy*dz, w2 = is_added?1:(1-w1);
12752  (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
12753  }
12754  if (nx>=0 && nx<width()) {
12755  const float w1 = dx*dy*dz, w2 = is_added?1:(1-w1);
12756  (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
12757  }
12758  }
12759  }
12760  }
12761  return *this;
12762  }
12763 
12765 
12777  CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
12778  if (is_empty()) return CImg<charT>::string("");
12779  CImgList<charT> items;
12780  char s_item[256] = { 0 };
12781  const T *ptrs = _data;
12782  unsigned int string_size = 0;
12783  for (unsigned long off = 0, siz = (unsigned int)size(); off<siz && string_size<=max_size; ++off) {
12784  const unsigned int printed_size = 1U + cimg_snprintf(s_item,sizeof(s_item),cimg::type<T>::format(),cimg::type<T>::format(*(ptrs++)));
12785  CImg<charT> item(s_item,printed_size);
12786  item[printed_size-1] = separator;
12787  item.move_to(items);
12788  if (max_size) string_size+=printed_size;
12789  }
12790  CImg<charT> res;
12791  (items>'x').move_to(res);
12792  if (max_size && res._width>max_size) res.crop(0,max_size);
12793  res.back() = 0;
12794  return res;
12795  }
12796 
12798  //-------------------------------------
12799  //
12801 
12802  //-------------------------------------
12803 
12805 
12812  bool is_shared() const {
12813  return _is_shared;
12814  }
12815 
12817 
12821  bool is_empty() const {
12822  return !(_data && _width && _height && _depth && _spectrum);
12823  }
12824 
12826 
12829  bool is_inf() const {
12830  if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
12831  return false;
12832  }
12833 
12835 
12838  bool is_nan() const {
12839  if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
12840  return false;
12841  }
12842 
12844  bool is_sameX(const unsigned int size_x) const {
12845  return _width==size_x;
12846  }
12847 
12849  template<typename t>
12850  bool is_sameX(const CImg<t>& img) const {
12851  return is_sameX(img._width);
12852  }
12853 
12855  bool is_sameX(const CImgDisplay& disp) const {
12856  return is_sameX(disp._width);
12857  }
12858 
12860  bool is_sameY(const unsigned int size_y) const {
12861  return _height==size_y;
12862  }
12863 
12865  template<typename t>
12866  bool is_sameY(const CImg<t>& img) const {
12867  return is_sameY(img._height);
12868  }
12869 
12871  bool is_sameY(const CImgDisplay& disp) const {
12872  return is_sameY(disp._height);
12873  }
12874 
12876  bool is_sameZ(const unsigned int size_z) const {
12877  return _depth==size_z;
12878  }
12879 
12881  template<typename t>
12882  bool is_sameZ(const CImg<t>& img) const {
12883  return is_sameZ(img._depth);
12884  }
12885 
12887  bool is_sameC(const unsigned int size_c) const {
12888  return _spectrum==size_c;
12889  }
12890 
12892  template<typename t>
12893  bool is_sameC(const CImg<t>& img) const {
12894  return is_sameC(img._spectrum);
12895  }
12896 
12898 
12901  bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
12902  return _width==size_x && _height==size_y;
12903  }
12904 
12906 
12909  template<typename t>
12910  bool is_sameXY(const CImg<t>& img) const {
12911  return is_sameXY(img._width,img._height);
12912  }
12913 
12915 
12918  bool is_sameXY(const CImgDisplay& disp) const {
12919  return is_sameXY(disp._width,disp._height);
12920  }
12921 
12923 
12926  bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
12927  return _width==size_x && _depth==size_z;
12928  }
12929 
12931 
12934  template<typename t>
12935  bool is_sameXZ(const CImg<t>& img) const {
12936  return is_sameXZ(img._width,img._depth);
12937  }
12938 
12940 
12943  bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
12944  return _width==size_x && _spectrum==size_c;
12945  }
12946 
12948 
12951  template<typename t>
12952  bool is_sameXC(const CImg<t>& img) const {
12953  return is_sameXC(img._width,img._spectrum);
12954  }
12955 
12957 
12960  bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
12961  return _height==size_y && _depth==size_z;
12962  }
12963 
12965 
12968  template<typename t>
12969  bool is_sameYZ(const CImg<t>& img) const {
12970  return is_sameYZ(img._height,img._depth);
12971  }
12972 
12974 
12977  bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
12978  return _height==size_y && _spectrum==size_c;
12979  }
12980 
12982 
12985  template<typename t>
12986  bool is_sameYC(const CImg<t>& img) const {
12987  return is_sameYC(img._height,img._spectrum);
12988  }
12989 
12991 
12994  bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
12995  return _depth==size_z && _spectrum==size_c;
12996  }
12997 
12999 
13002  template<typename t>
13003  bool is_sameZC(const CImg<t>& img) const {
13004  return is_sameZC(img._depth,img._spectrum);
13005  }
13006 
13008 
13011  bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
13012  return is_sameXY(size_x,size_y) && _depth==size_z;
13013  }
13014 
13016 
13019  template<typename t>
13020  bool is_sameXYZ(const CImg<t>& img) const {
13021  return is_sameXYZ(img._width,img._height,img._depth);
13022  }
13023 
13025 
13028  bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
13029  return is_sameXY(size_x,size_y) && _spectrum==size_c;
13030  }
13031 
13033 
13036  template<typename t>
13037  bool is_sameXYC(const CImg<t>& img) const {
13038  return is_sameXYC(img._width,img._height,img._spectrum);
13039  }
13040 
13042 
13045  bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
13046  return is_sameXZ(size_x,size_z) && _spectrum==size_c;
13047  }
13048 
13050 
13053  template<typename t>
13054  bool is_sameXZC(const CImg<t>& img) const {
13055  return is_sameXZC(img._width,img._depth,img._spectrum);
13056  }
13057 
13059 
13062  bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
13063  return is_sameYZ(size_y,size_z) && _spectrum==size_c;
13064  }
13065 
13067 
13070  template<typename t>
13071  bool is_sameYZC(const CImg<t>& img) const {
13072  return is_sameYZC(img._height,img._depth,img._spectrum);
13073  }
13074 
13076 
13079  bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
13080  return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
13081  }
13082 
13084 
13087  template<typename t>
13088  bool is_sameXYZC(const CImg<t>& img) const {
13089  return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
13090  }
13091 
13093 
13107  bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
13108  return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
13109  }
13110 
13112 
13131  template<typename t>
13132  bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
13133  const unsigned long wh = (unsigned long)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
13134  const T *const ppixel = &pixel;
13135  if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
13136  unsigned long off = (unsigned long)(ppixel - _data);
13137  const unsigned long nc = off/whd;
13138  off%=whd;
13139  const unsigned long nz = off/wh;
13140  off%=wh;
13141  const unsigned long ny = off/_width, nx = off%_width;
13142  x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
13143  return true;
13144  }
13145 
13147 
13150  template<typename t>
13151  bool contains(const T& pixel, t& x, t& y, t& z) const {
13152  const unsigned long wh = (unsigned long)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
13153  const T *const ppixel = &pixel;
13154  if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
13155  unsigned long off = ((unsigned long)(ppixel - _data))%whd;
13156  const unsigned long nz = off/wh;
13157  off%=wh;
13158  const unsigned long ny = off/_width, nx = off%_width;
13159  x = (t)nx; y = (t)ny; z = (t)nz;
13160  return true;
13161  }
13162 
13164 
13167  template<typename t>
13168  bool contains(const T& pixel, t& x, t& y) const {
13169  const unsigned long wh = (unsigned long)_width*_height, siz = wh*_depth*_spectrum;
13170  const T *const ppixel = &pixel;
13171  if (is_empty() || ppixel<_data || ppixel>=_data+siz) return false;
13172  unsigned long off = ((unsigned int)(ppixel - _data))%wh;
13173  const unsigned long ny = off/_width, nx = off%_width;
13174  x = (t)nx; y = (t)ny;
13175  return true;
13176  }
13177 
13179 
13182  template<typename t>
13183  bool contains(const T& pixel, t& x) const {
13184  const T *const ppixel = &pixel;
13185  if (is_empty() || ppixel<_data || ppixel>=_data+size()) return false;
13186  x = (t)(((unsigned long)(ppixel - _data))%_width);
13187  return true;
13188  }
13189 
13191 
13194  bool contains(const T& pixel) const {
13195  const T *const ppixel = &pixel;
13196  return !is_empty() && ppixel>=_data && ppixel<_data + size();
13197  }
13198 
13200 
13217  template<typename t>
13218  bool is_overlapped(const CImg<t>& img) const {
13219  const unsigned long csiz = size(), isiz = img.size();
13220  return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
13221  }
13222 
13224 
13237  template<typename tp, typename tc, typename to>
13238  bool is_object3d(const CImgList<tp>& primitives,
13239  const CImgList<tc>& colors,
13240  const to& opacities,
13241  const bool is_full_check=true,
13242  char *const error_message=0) const {
13243  if (error_message) *error_message = 0;
13244 
13245  // Check consistency for the particular case of an empty 3d object.
13246  if (is_empty()) {
13247  if (primitives || colors || opacities) {
13248  if (error_message) std::sprintf(error_message,
13249  "3d object has no vertices but %u primitives, %u colors and %lu opacities",
13250  primitives._width,colors._width,(unsigned long)opacities.size());
13251  return false;
13252  }
13253  return true;
13254  }
13255 
13256  // Check consistency of vertices.
13257  if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions.
13258  if (error_message) std::sprintf(error_message,
13259  "3d object (%u,%u) has invalid vertices dimensions (%u,%u,%u,%u)",
13260  _width,primitives._width,_width,_height,_depth,_spectrum);
13261  return false;
13262  }
13263  if (colors._width>primitives._width+1) {
13264  if (error_message) std::sprintf(error_message,
13265  "3d object (%u,%u) defines %u colors",
13266  _width,primitives._width,colors._width);
13267  return false;
13268  }
13269  if (opacities.size()>primitives._width) {
13270  if (error_message) std::sprintf(error_message,
13271  "3d object (%u,%u) defines %lu opacities",
13272  _width,primitives._width,(unsigned long)opacities.size());
13273  return false;
13274  }
13275  if (!is_full_check) return true;
13276 
13277  // Check consistency of primitives.
13278  cimglist_for(primitives,l) {
13279  const CImg<tp>& primitive = primitives[l];
13280  const unsigned long psiz = primitive.size();
13281  switch (psiz) {
13282  case 1 : { // Point.
13283  const unsigned int i0 = (unsigned int)primitive(0);
13284  if (i0>=_width) {
13285  if (error_message) std::sprintf(error_message,
13286  "3d object (%u,%u) refers to invalid vertex indice %u in point primitive %u",
13287  _width,primitives._width,i0,l);
13288  return false;
13289  }
13290  } break;
13291  case 5 : { // Sphere.
13292  const unsigned int
13293  i0 = (unsigned int)primitive(0),
13294  i1 = (unsigned int)primitive(1);
13295  if (i0>=_width || i1>=_width) {
13296  if (error_message) std::sprintf(error_message,
13297  "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u",
13298  _width,primitives._width,i0,i1,l);
13299  return false;
13300  }
13301  } break;
13302  case 2 : // Segment.
13303  case 6 : {
13304  const unsigned int
13305  i0 = (unsigned int)primitive(0),
13306  i1 = (unsigned int)primitive(1);
13307  if (i0>=_width || i1>=_width) {
13308  if (error_message) std::sprintf(error_message,
13309  "3d object (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u",
13310  _width,primitives._width,i0,i1,l);
13311  return false;
13312  }
13313  } break;
13314  case 3 : // Triangle.
13315  case 9 : {
13316  const unsigned int
13317  i0 = (unsigned int)primitive(0),
13318  i1 = (unsigned int)primitive(1),
13319  i2 = (unsigned int)primitive(2);
13320  if (i0>=_width || i1>=_width || i2>=_width) {
13321  if (error_message) std::sprintf(error_message,
13322  "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u",
13323  _width,primitives._width,i0,i1,i2,l);
13324  return false;
13325  }
13326  } break;
13327  case 4 : // Quadrangle.
13328  case 12 : {
13329  const unsigned int
13330  i0 = (unsigned int)primitive(0),
13331  i1 = (unsigned int)primitive(1),
13332  i2 = (unsigned int)primitive(2),
13333  i3 = (unsigned int)primitive(3);
13334  if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
13335  if (error_message) std::sprintf(error_message,
13336  "3d object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u",
13337  _width,primitives._width,i0,i1,i2,i3,l);
13338  return false;
13339  }
13340  } break;
13341  default :
13342  if (error_message) std::sprintf(error_message,
13343  "3d object has invalid primitive %u of size %u",
13344  l,(unsigned int)psiz);
13345  return false;
13346  }
13347  }
13348 
13349  // Check consistency of colors.
13350  cimglist_for(colors,c) {
13351  const CImg<tc>& color = colors[c];
13352  if (!color) {
13353  if (error_message) std::sprintf(error_message,
13354  "3d object has empty color for primitive %u",
13355  c);
13356  return false;
13357  }
13358  }
13359 
13360  // Check consistency of light texture.
13361  if (colors._width>primitives._width) {
13362  const CImg<tc> &light = colors.back();
13363  if (!light || light._depth>1) {
13364  if (error_message) std::sprintf(error_message,
13365  "3d object has invalid light texture (%u,%u,%u,%u)",
13366  light._width,light._height,light._depth,light._spectrum);
13367  return false;
13368  }
13369  }
13370 
13371  return true;
13372  }
13373 
13375 
13384  bool is_CImg3d(const bool is_full_check=true, char *const error_message=0) const {
13385  if (error_message) *error_message = 0;
13386 
13387  // Check instance dimension and header.
13388  if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
13389  if (error_message) std::sprintf(error_message,
13390  "CImg3d has invalid dimensions (%u,%u,%u,%u)",
13391  _width,_height,_depth,_spectrum);
13392  return false;
13393  }
13394  const T *ptrs = _data, *const ptre = end();
13395  if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
13396  !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
13397  if (error_message) std::sprintf(error_message,
13398  "CImg3d header not found");
13399  return false;
13400  }
13401  const unsigned int
13402  nb_points = cimg::float2uint((float)*(ptrs++)),
13403  nb_primitives = cimg::float2uint((float)*(ptrs++));
13404 
13405  // Check consistency of vertex data.
13406  if (!nb_points) {
13407  if (nb_primitives) {
13408  if (error_message) std::sprintf(error_message,
13409  "CImg3d has no vertices but %u primitives",
13410  nb_primitives);
13411  return false;
13412  }
13413  if (ptrs!=ptre) {
13414  if (error_message) std::sprintf(error_message,
13415  "CImg3d (%u,%u) is empty but contains %u byte%s more than expected",
13416  nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
13417  return false;
13418  }
13419  return true;
13420  }
13421  ptrs+=3*nb_points;
13422  if (ptrs>ptre) {
13423  if (error_message) std::sprintf(error_message,
13424  "CImg3d (%u,%u) has incomplete vertex data",
13425  nb_points,nb_primitives);
13426  return false;
13427  }
13428  if (!is_full_check) return true;
13429 
13430  // Check consistency of primitive data.
13431  if (ptrs==ptre) {
13432  if (error_message) std::sprintf(error_message,
13433  "CImg3d (%u,%u) has no primitive data",
13434  nb_points,nb_primitives);
13435  return false;
13436  }
13437  for (unsigned int p = 0; p<nb_primitives; ++p) {
13438  const unsigned int nb_inds = (unsigned int)*(ptrs++);
13439  switch (nb_inds) {
13440  case 1 : { // Point.
13441  const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
13442  if (i0>=nb_points) {
13443  if (error_message) std::sprintf(error_message,
13444  "CImg3d (%u,%u) refers to invalid vertex indice %u in point primitive %u",
13445  nb_points,nb_primitives,i0,p);
13446  return false;
13447  }
13448  } break;
13449  case 5 : { // Sphere.
13450  const unsigned int
13451  i0 = cimg::float2uint((float)*(ptrs++)),
13452  i1 = cimg::float2uint((float)*(ptrs++));
13453  ptrs+=3;
13454  if (i0>=nb_points || i1>=nb_points) {
13455  if (error_message) std::sprintf(error_message,
13456  "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in sphere primitive %u",
13457  nb_points,nb_primitives,i0,i1,p);
13458  return false;
13459  }
13460  } break;
13461  case 2 : case 6 : { // Segment.
13462  const unsigned int
13463  i0 = cimg::float2uint((float)*(ptrs++)),
13464  i1 = cimg::float2uint((float)*(ptrs++));
13465  if (nb_inds==6) ptrs+=4;
13466  if (i0>=nb_points || i1>=nb_points) {
13467  if (error_message) std::sprintf(error_message,
13468  "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in segment primitive %u",
13469  nb_points,nb_primitives,i0,i1,p);
13470  return false;
13471  }
13472  } break;
13473  case 3 : case 9 : { // Triangle.
13474  const unsigned int
13475  i0 = cimg::float2uint((float)*(ptrs++)),
13476  i1 = cimg::float2uint((float)*(ptrs++)),
13477  i2 = cimg::float2uint((float)*(ptrs++));
13478  if (nb_inds==9) ptrs+=6;
13479  if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
13480  if (error_message) std::sprintf(error_message,
13481  "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in triangle primitive %u",
13482  nb_points,nb_primitives,i0,i1,i2,p);
13483  return false;
13484  }
13485  } break;
13486  case 4 : case 12 : { // Quadrangle.
13487  const unsigned int
13488  i0 = cimg::float2uint((float)*(ptrs++)),
13489  i1 = cimg::float2uint((float)*(ptrs++)),
13490  i2 = cimg::float2uint((float)*(ptrs++)),
13491  i3 = cimg::float2uint((float)*(ptrs++));
13492  if (nb_inds==12) ptrs+=8;
13493  if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
13494  if (error_message) std::sprintf(error_message,
13495  "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in quadrangle primitive %u",
13496  nb_points,nb_primitives,i0,i1,i2,i3,p);
13497  return false;
13498  }
13499  } break;
13500  default :
13501  if (error_message) std::sprintf(error_message,
13502  "CImg3d (%u,%u) has invalid primitive %u of size %u",
13503  nb_points,nb_primitives,p,nb_inds);
13504  return false;
13505  }
13506  if (ptrs>ptre) {
13507  if (error_message) std::sprintf(error_message,
13508  "CImg3d (%u,%u) has incomplete primitive data for primitive %u",
13509  nb_points,nb_primitives,p);
13510  return false;
13511  }
13512  }
13513 
13514  // Check consistency of color data.
13515  if (ptrs==ptre) {
13516  if (error_message) std::sprintf(error_message,
13517  "CImg3d (%u,%u) has no color/texture data",
13518  nb_points,nb_primitives);
13519  return false;
13520  }
13521  for (unsigned int c = 0; c<nb_primitives; ++c) {
13522  if (*(ptrs++)!=(T)-128) ptrs+=2;
13523  else if ((ptrs+=3)<ptre) {
13524  const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
13525  if (!h && !s) {
13526  if (w>=c) {
13527  if (error_message) std::sprintf(error_message,
13528  "CImg3d (%u,%u) refers to invalid shared sprite/texture indice %u for primitive %u",
13529  nb_points,nb_primitives,w,c);
13530  return false;
13531  }
13532  } else ptrs+=w*h*s;
13533  }
13534  if (ptrs>ptre) {
13535  if (error_message) std::sprintf(error_message,
13536  "CImg3d (%u,%u) has incomplete color/texture data for primitive %u",
13537  nb_points,nb_primitives,c);
13538  return false;
13539  }
13540  }
13541 
13542  // Check consistency of opacity data.
13543  if (ptrs==ptre) {
13544  if (error_message) std::sprintf(error_message,
13545  "CImg3d (%u,%u) has no opacity data",
13546  nb_points,nb_primitives);
13547  return false;
13548  }
13549  for (unsigned int o = 0; o<nb_primitives; ++o) {
13550  if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
13551  const unsigned int w = (unsigned int)*(ptrs-3), h = (unsigned int)*(ptrs-2), s = (unsigned int)*(ptrs-1);
13552  if (!h && !s) {
13553  if (w>=o) {
13554  if (error_message) std::sprintf(error_message,
13555  "CImg3d (%u,%u) refers to invalid shared opacity indice %u for primitive %u",
13556  nb_points,nb_primitives,w,o);
13557  return false;
13558  }
13559  } else ptrs+=w*h*s;
13560  }
13561  if (ptrs>ptre) {
13562  if (error_message) std::sprintf(error_message,
13563  "CImg3d (%u,%u) has incomplete opacity data for primitive %u",
13564  nb_points,nb_primitives,o);
13565  return false;
13566  }
13567  }
13568 
13569  // Check end of data.
13570  if (ptrs<ptre) {
13571  if (error_message) std::sprintf(error_message,
13572  "CImg3d (%u,%u) contains %u byte%s more than expected",
13573  nb_points,nb_primitives,(unsigned int)(ptre-ptrs),(ptre-ptrs)>1?"s":"");
13574  return false;
13575  }
13576  return true;
13577  }
13578 
13579  static bool _is_CImg3d(const T val, const char c) {
13580  return val>=(T)c && val<(T)(c+1);
13581  }
13582 
13584  //-------------------------------------
13585  //
13587 
13588  //-------------------------------------
13589 
13590  // Define the math formula parser/compiler and evaluator.
13592  CImgList<charT> label;
13593  CImgList<uintT> code;
13594  CImg<uintT> level, opcode;
13595  CImg<doubleT> mem;
13596  CImg<charT> expr;
13597  const CImg<T>& reference;
13598  CImg<Tdouble> reference_stats;
13599  unsigned int mempos, result;
13600  const char *const calling_function;
13601 #define _cimg_mp_return(x) { *se = saved_char; return x; }
13602 #define _cimg_mp_opcode0(op) _cimg_mp_return(opcode0(op));
13603 #define _cimg_mp_opcode1(op,i1) _cimg_mp_return(opcode1(op,i1));
13604 #define _cimg_mp_opcode2(op,i1,i2) { const unsigned int _i1 = i1, _i2 = i2; _cimg_mp_return(opcode2(op,_i1,_i2)); }
13605 #define _cimg_mp_opcode3(op,i1,i2,i3) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3; _cimg_mp_return(opcode3(op,_i1,_i2,_i3)); }
13606 #define _cimg_mp_opcode6(op,i1,i2,i3,i4,i5,i6) { const unsigned int _i1 = i1, _i2 = i2, _i3 = i3, _i4 = i4, _i5 = i5, _i6 = i6; \
13607  _cimg_mp_return(opcode6(op,_i1,_i2,_i3,_i4,_i5,_i6)); }
13608 
13609  // Constructor - Destructor.
13610  _cimg_math_parser(const CImg<T>& img, const char *const expression, const char *const funcname=0):
13611  reference(img),calling_function(funcname?funcname:"cimg_math_parser") {
13612  unsigned int l = 0;
13613  if (expression) {
13614  l = (unsigned int)std::strlen(expression);
13615  expr.assign(expression,l+1);
13616  if (*expr._data) {
13617  char *d = expr._data;
13618  for (const char *s = expr._data; *s || (bool)(*d=0); ++s) if (*s!=' ') *(d++) = *s;
13619  l = (unsigned int)(d - expr._data);
13620  }
13621  }
13622  if (!l) throw CImgArgumentException("[_cimg_math_parser] "
13623  "CImg<%s>::%s(): Empty specified expression.",
13624  pixel_type(),calling_function);
13625 
13626  int lv = 0; // Count parenthesis level of expression.
13627  level.assign(l);
13628  unsigned int *pd = level._data;
13629  for (const char *ps = expr._data; *ps && lv>=0; ++ps) *(pd++) = (unsigned int)(*ps=='('?lv++:*ps==')'?--lv:lv);
13630  if (lv!=0) {
13631  throw CImgArgumentException("[_cimg_math_parser] "
13632  "CImg<%s>::%s(): Unbalanced parentheses in specified expression '%s'.",
13633  pixel_type(),calling_function,
13634  expr._data);
13635  }
13636  // Init constant values.
13637  mem.assign(512);
13638  label.assign(512);
13639  mem[0] = 0;
13640  mem[1] = 1;
13641  mem[2] = (double)reference._width;
13642  mem[3] = (double)reference._height;
13643  mem[4] = (double)reference._depth;
13644  mem[5] = (double)reference._spectrum;
13645  mem[6] = cimg::PI;
13646  mem[7] = std::exp(1.0); // Then [8] = x, [9] = y, [10] = z, [11] = c
13647  mempos = 12;
13648  result = compile(expr._data,expr._data+l); // Compile formula into a serie of opcodes.
13649  }
13650 
13651  // Insert code instructions.
13652  unsigned int opcode0(const char op) {
13653  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13654  const unsigned int pos = mempos++;
13655  CImg<uintT>::vector(op,pos).move_to(code);
13656  return pos;
13657  }
13658 
13659  unsigned int opcode1(const char op, const unsigned int arg1) {
13660  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13661  const unsigned int pos = mempos++;
13662  CImg<uintT>::vector(op,pos,arg1).move_to(code);
13663  return pos;
13664  }
13665 
13666  unsigned int opcode2(const char op, const unsigned int arg1, const unsigned int arg2) {
13667  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13668  const unsigned int pos = mempos++;
13669  CImg<uintT>::vector(op,pos,arg1,arg2).move_to(code);
13670  return pos;
13671  }
13672 
13673  unsigned int opcode3(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
13674  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13675  const unsigned int pos = mempos++;
13676  CImg<uintT>::vector(op,pos,arg1,arg2,arg3).move_to(code);
13677  return pos;
13678  }
13679 
13680  unsigned int opcode6(const char op, const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
13681  const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
13682  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13683  const unsigned int pos = mempos++;
13684  CImg<uintT>::vector(op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
13685  return pos;
13686  }
13687 
13688  // Compilation procedure.
13689  unsigned int compile(char *const ss, char *const se) {
13690  if (!ss || se<=ss || !*ss) {
13691  throw CImgArgumentException("[_cimg_math_parser] "
13692  "CImg<%s>::%s(): Missing item in specified expression '%s'.",
13693  pixel_type(),calling_function,
13694  expr._data);
13695  }
13696  char
13697  *const se1 = se-1, *const se2 = se-2, *const se3 = se-3, *const se4 = se-4,
13698  *const ss1 = ss+1, *const ss2 = ss+2, *const ss3 = ss+3, *const ss4 = ss+4,
13699  *const ss5 = ss+5, *const ss6 = ss+6, *const ss7 = ss+7;
13700  const char saved_char = *se; *se = 0;
13701  const unsigned int clevel = level[ss-expr._data], clevel1 = clevel+1;
13702  if (*se1==';') return compile(ss,se1);
13703 
13704  // Look for a single value, variable or variable assignment.
13705  char end = 0, sep = 0; double val = 0;
13706  const int nb = std::sscanf(ss,"%lf%c%c",&val,&sep,&end);
13707  if (nb==1) {
13708  if (val==0) _cimg_mp_return(0);
13709  if (val==1) _cimg_mp_return(1);
13710  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13711  const unsigned int pos = mempos++;
13712  mem[pos] = val;
13713  _cimg_mp_return(pos);
13714  }
13715  if (nb==2 && sep=='%') {
13716  if (val==0) _cimg_mp_return(0);
13717  if (val==100) _cimg_mp_return(1);
13718  if (mempos>=mem._width) mem.resize(-200,1,1,1,0);
13719  const unsigned int pos = mempos++;
13720  mem[pos] = val/100;
13721  _cimg_mp_return(pos);
13722  }
13723  if (ss1==se) switch (*ss) {
13724  case 'w' : _cimg_mp_return(2); case 'h' : _cimg_mp_return(3); case 'd' : _cimg_mp_return(4); case 's' : _cimg_mp_return(5);
13725  case 'x' : _cimg_mp_return(8); case 'y' : _cimg_mp_return(9); case 'z' : _cimg_mp_return(10); case 'c' : _cimg_mp_return(11);
13726  case 'e' : _cimg_mp_return(7);
13727  case 'u' : case '?' : _cimg_mp_opcode2(0,0,1);
13728  case 'g' : _cimg_mp_opcode0(1);
13729  case 'i' : _cimg_mp_opcode0(2);
13730  }
13731  if (ss1==se1) {
13732  if (*ss=='p' && *ss1=='i') _cimg_mp_return(6); // pi
13733  if (*ss=='i') {
13734  if (*ss1=='m') _cimg_mp_opcode0(57); // im
13735  if (*ss1=='M') _cimg_mp_opcode0(58); // iM
13736  if (*ss1=='a') _cimg_mp_opcode0(59); // ia
13737  if (*ss1=='v') _cimg_mp_opcode0(60); // iv
13738  }
13739  if (*ss1=='m') {
13740  if (*ss=='x') _cimg_mp_opcode0(61); // xm
13741  if (*ss=='y') _cimg_mp_opcode0(62); // ym
13742  if (*ss=='z') _cimg_mp_opcode0(63); // zm
13743  if (*ss=='c') _cimg_mp_opcode0(64); // cm
13744  }
13745  if (*ss1=='M') {
13746  if (*ss=='x') _cimg_mp_opcode0(65); // xM
13747  if (*ss=='y') _cimg_mp_opcode0(66); // yM
13748  if (*ss=='z') _cimg_mp_opcode0(67); // zM
13749  if (*ss=='c') _cimg_mp_opcode0(68); // cM
13750  }
13751  }
13752  if (ss3==se) {
13753  if (*ss=='x' && *ss1=='/' && *ss2=='w') _cimg_mp_opcode0(3);
13754  if (*ss=='y' && *ss1=='/' && *ss2=='h') _cimg_mp_opcode0(4);
13755  if (*ss=='z' && *ss1=='/' && *ss2=='d') _cimg_mp_opcode0(5);
13756  if (*ss=='c' && *ss1=='/' && *ss2=='s') _cimg_mp_opcode0(6);
13757  }
13758 
13759  // Look for variable declarations.
13760  for (char *s = se2; s>ss; --s) if (*s==';' && level[s-expr._data]==clevel) { compile(ss,s); _cimg_mp_return(compile(s+1,se)); }
13761  for (char *s = ss1, *ps = ss, *ns = ss2; s<se1; ++s, ++ps, ++ns)
13762  if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' && level[s-expr._data]==clevel) {
13763  CImg<charT> variable_name(ss,(unsigned int)(s-ss+1));
13764  variable_name.back() = 0;
13765  bool is_valid_name = true;
13766  if ((*ss>='0' && *ss<='9') ||
13767  (s==ss+1 && (*ss=='x' || *ss=='y' || *ss=='z' || *ss=='c' ||
13768  *ss=='w' || *ss=='h' || *ss=='d' || *ss=='s' ||
13769  *ss=='e' || *ss=='u' || *ss=='g' || *ss=='i')) ||
13770  (s==ss+2 && ((*ss=='p' && *(ss+1)=='i') ||
13771  (*ss=='i' && (*(ss+1)=='m' || *(ss+1)=='M' || *(ss+1)=='a' || *(ss+1)=='v')) ||
13772  (*ss=='x' && (*(ss+1)=='m' || *(ss+1)=='M')) ||
13773  (*ss=='y' && (*(ss+1)=='m' || *(ss+1)=='M')) ||
13774  (*ss=='z' && (*(ss+1)=='m' || *(ss+1)=='M')) ||
13775  (*ss=='c' && (*(ss+1)=='m' || *(ss+1)=='M'))))) is_valid_name = false;
13776  for (const char *ns = ss; ns<s; ++ns)
13777  if ((*ns<'a' || *ns>'z') && (*ns<'A' || *ns>'Z') && (*ns<'0' || *ns>'9') && *ns!='_') {
13778  is_valid_name = false; break;
13779  }
13780  if (!is_valid_name) {
13781  *se = saved_char;
13782  if (!std::strcmp(variable_name,"x") || !std::strcmp(variable_name,"y") || !std::strcmp(variable_name,"z") ||
13783  !std::strcmp(variable_name,"c") || !std::strcmp(variable_name,"w") || !std::strcmp(variable_name,"h") ||
13784  !std::strcmp(variable_name,"d") || !std::strcmp(variable_name,"s") || !std::strcmp(variable_name,"e") ||
13785  !std::strcmp(variable_name,"u") || !std::strcmp(variable_name,"g") || !std::strcmp(variable_name,"i") ||
13786  !std::strcmp(variable_name,"pi") || !std::strcmp(variable_name,"im") || !std::strcmp(variable_name,"iM") ||
13787  !std::strcmp(variable_name,"ia") || !std::strcmp(variable_name,"iv") ||
13788  !std::strcmp(variable_name,"xm") || !std::strcmp(variable_name,"ym") ||
13789  !std::strcmp(variable_name,"zm") || !std::strcmp(variable_name,"cm") ||
13790  !std::strcmp(variable_name,"xM") || !std::strcmp(variable_name,"yM") ||
13791  !std::strcmp(variable_name,"zM") || !std::strcmp(variable_name,"cM"))
13792  throw CImgArgumentException("[_cimg_math_parser] "
13793  "CImg<%s>::%s(): Invalid assignment of reserved variable name '%s' in specified expression '%s'.",
13794  pixel_type(),calling_function,
13795  variable_name._data,expr._data);
13796  else
13797  throw CImgArgumentException("[_cimg_math_parser] "
13798  "CImg<%s>::%s(): Invalid variable name '%s' in specified expression '%s'.",
13799  pixel_type(),calling_function,
13800  variable_name._data,expr._data);
13801  }
13802  for (unsigned int i = 0; i<mempos; ++i) // Check for existing variable with same name.
13803  if (label[i]._data && !std::strcmp(variable_name,label[i])) {
13804  *se = saved_char;
13805  throw CImgArgumentException("[_cimg_math_parser] "
13806  "CImg<%s>::%s(): Invalid multiple assignments of variable '%s' in specified expression '%s'.",
13807  pixel_type(),calling_function,
13808  variable_name._data,expr._data);
13809  }
13810  const unsigned int src_pos = compile(s+1,se);
13811  if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13812  const unsigned int dest_pos = mempos++;
13813  variable_name.move_to(label[dest_pos]);
13814  CImg<uintT>::vector(7,dest_pos,src_pos).move_to(code);
13815  _cimg_mp_return(dest_pos);
13816  }
13817 
13818  // Look for unary/binary operators. The operator precedences is defined as in C++.
13819  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='|' && *ns=='|' && level[s-expr._data]==clevel) _cimg_mp_opcode2(8,compile(ss,s),compile(s+2,se));
13820  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='&' && *ns=='&' && level[s-expr._data]==clevel) _cimg_mp_opcode2(9,compile(ss,s),compile(s+2,se));
13821  for (char *s = se2; s>ss; --s) if (*s=='|' && level[s-expr._data]==clevel) _cimg_mp_opcode2(10,compile(ss,s),compile(s+1,se));
13822  for (char *s = se2; s>ss; --s) if (*s=='&' && level[s-expr._data]==clevel) _cimg_mp_opcode2(11,compile(ss,s),compile(s+1,se));
13823  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='!' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(12,compile(ss,s),compile(s+2,se));
13824  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='=' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(13,compile(ss,s),compile(s+2,se));
13825  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(14,compile(ss,s),compile(s+2,se));
13826  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='=' && level[s-expr._data]==clevel) _cimg_mp_opcode2(15,compile(ss,s),compile(s+2,se));
13827  for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
13828  if (*s=='<' && *ns!='<' && *ps!='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(16,compile(ss,s),compile(s+1,se));
13829  for (char *s = se2, *ns = se1, *ps = se3; s>ss; --s, --ns, --ps)
13830  if (*s=='>' && *ns!='>' && *ps!='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(17,compile(ss,s),compile(s+1,se));
13831  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='<' && *ns=='<' && level[s-expr._data]==clevel) _cimg_mp_opcode2(18,compile(ss,s),compile(s+2,se));
13832  for (char *s = se3, *ns = se2; s>ss; --s, --ns) if (*s=='>' && *ns=='>' && level[s-expr._data]==clevel) _cimg_mp_opcode2(19,compile(ss,s),compile(s+2,se));
13833  for (char *s = se2, *ps = se3; s>ss; --s, --ps)
13834  if (*s=='+' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
13835  (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
13836  _cimg_mp_opcode2(21,compile(ss,s),compile(s+1,se));
13837  for (char *s = se2, *ps = se3; s>ss; --s, --ps)
13838  if (*s=='-' && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' && *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' &&
13839  (*ps!='e' || !(ps>ss && (*(ps-1)=='.' || (*(ps-1)>='0' && *(ps-1)<='9')))) && level[s-expr._data]==clevel)
13840  _cimg_mp_opcode2(20,compile(ss,s),compile(s+1,se));
13841  for (char *s = se2; s>ss; --s) if (*s=='*' && level[s-expr._data]==clevel) _cimg_mp_opcode2(22,compile(ss,s),compile(s+1,se));
13842  for (char *s = se2; s>ss; --s) if (*s=='/' && level[s-expr._data]==clevel) _cimg_mp_opcode2(23,compile(ss,s),compile(s+1,se));
13843  for (char *s = se2, *ns = se1; s>ss; --s, --ns)
13844  if (*s=='%' && *ns!='^' && level[s-expr._data]==clevel)
13845  _cimg_mp_opcode2(24,compile(ss,s),compile(s+1,se));
13846  if (ss<se1) {
13847  if (*ss=='+') _cimg_mp_return(compile(ss1,se));
13848  if (*ss=='-') _cimg_mp_opcode1(26,compile(ss1,se));
13849  if (*ss=='!') _cimg_mp_opcode1(27,compile(ss1,se));
13850  if (*ss=='~') _cimg_mp_opcode1(28,compile(ss1,se));
13851  }
13852  for (char *s = se2; s>ss; --s) if (*s=='^' && level[s-expr._data]==clevel) _cimg_mp_opcode2(25,compile(ss,s),compile(s+1,se));
13853 
13854  // Look for a function call or a parenthesis.
13855  if (*se1==')') {
13856  if (*ss=='(') _cimg_mp_return(compile(ss1,se1));
13857  if (!std::strncmp(ss,"sin(",4)) _cimg_mp_opcode1(29,compile(ss4,se1));
13858  if (!std::strncmp(ss,"cos(",4)) _cimg_mp_opcode1(30,compile(ss4,se1));
13859  if (!std::strncmp(ss,"tan(",4)) _cimg_mp_opcode1(31,compile(ss4,se1));
13860  if (!std::strncmp(ss,"asin(",5)) _cimg_mp_opcode1(32,compile(ss5,se1));
13861  if (!std::strncmp(ss,"acos(",5)) _cimg_mp_opcode1(33,compile(ss5,se1));
13862  if (!std::strncmp(ss,"atan(",5)) _cimg_mp_opcode1(34,compile(ss5,se1));
13863  if (!std::strncmp(ss,"sinh(",5)) _cimg_mp_opcode1(35,compile(ss5,se1));
13864  if (!std::strncmp(ss,"cosh(",5)) _cimg_mp_opcode1(36,compile(ss5,se1));
13865  if (!std::strncmp(ss,"tanh(",5)) _cimg_mp_opcode1(37,compile(ss5,se1));
13866  if (!std::strncmp(ss,"log10(",6)) _cimg_mp_opcode1(38,compile(ss6,se1));
13867  if (!std::strncmp(ss,"log2(",5)) _cimg_mp_opcode1(71,compile(ss5,se1));
13868  if (!std::strncmp(ss,"log(",4)) _cimg_mp_opcode1(39,compile(ss4,se1));
13869  if (!std::strncmp(ss,"exp(",4)) _cimg_mp_opcode1(40,compile(ss4,se1));
13870  if (!std::strncmp(ss,"sqrt(",5)) _cimg_mp_opcode1(41,compile(ss5,se1));
13871  if (!std::strncmp(ss,"sign(",5)) _cimg_mp_opcode1(42,compile(ss5,se1));
13872  if (!std::strncmp(ss,"abs(",4)) _cimg_mp_opcode1(43,compile(ss4,se1));
13873  if (!std::strncmp(ss,"atan2(",6)) {
13874  char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13875  _cimg_mp_opcode2(44,compile(ss6,s1),compile(s1+1,se1));
13876  }
13877  if (!std::strncmp(ss,"if(",3)) {
13878  char *s1 = ss3; while (s1<se4 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13879  char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13880  _cimg_mp_opcode3(45,compile(ss3,s1),compile(s1+1,s2),compile(s2+1,se1));
13881  }
13882  if (!std::strncmp(ss,"round(",6)) {
13883  unsigned int value = 0, round = 1, direction = 0;
13884  char *s1 = ss6; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13885  value = compile(ss6,s1==se2?++s1:s1);
13886  if (s1<se1) {
13887  char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13888  round = compile(s1+1,s2==se2?++s2:s2);
13889  if (s2<se1) direction = compile(s2+1,se1);
13890  }
13891  _cimg_mp_opcode3(46,value,round,direction);
13892  }
13893  if (!std::strncmp(ss,"?(",2) || !std::strncmp(ss,"u(",2)) {
13894  if (*ss2==')') _cimg_mp_opcode2(0,0,1);
13895  char *s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13896  if (s1<se1) _cimg_mp_opcode2(0,compile(ss2,s1),compile(s1+1,se1));
13897  _cimg_mp_opcode2(0,0,compile(ss2,s1));
13898  }
13899  if (!std::strncmp(ss,"i(",2)) {
13900  if (*ss2==')') _cimg_mp_return(0);
13901  unsigned int indx = 8, indy = 9, indz = 10, indc = 11, borders = 0, interpolation = 0;
13902  if (ss2!=se1) {
13903  char *s1 = ss2; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13904  indx = compile(ss2,s1==se2?++s1:s1);
13905  if (s1<se1) {
13906  char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13907  indy = compile(s1+1,s2==se2?++s2:s2);
13908  if (s2<se1) {
13909  char *s3 = s2+1; while (s3<se2 && (*s3!=',' || level[s3-expr._data]!=clevel1)) ++s3;
13910  indz = compile(s2+1,s3==se2?++s3:s3);
13911  if (s3<se1) {
13912  char *s4 = s3+1; while (s4<se2 && (*s4!=',' || level[s4-expr._data]!=clevel1)) ++s4;
13913  indc = compile(s3+1,s4==se2?++s4:s4);
13914  if (s4<se1) {
13915  char *s5 = s4+1; while (s5<se2 && (*s5!=',' || level[s5-expr._data]!=clevel1)) ++s5;
13916  interpolation = compile(s4+1,s5==se2?++s5:s5);
13917  if (s5<se1) borders = compile(s5+1,se1);
13918  }
13919  }
13920  }
13921  }
13922  }
13923  _cimg_mp_opcode6(47,indx,indy,indz,indc,interpolation,borders);
13924  }
13925  if (!std::strncmp(ss,"min(",4) || !std::strncmp(ss,"max(",4)) {
13926  CImgList<uintT> opcode;
13927  if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13928  const unsigned int pos = mempos++;
13929  CImg<uintT>::vector(ss[1]=='i'?48:49,pos).move_to(opcode);
13930  for (char *s = ss4; s<se; ++s) {
13931  char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
13932  CImg<uintT>::vector(compile(s,ns)).move_to(opcode);
13933  s = ns;
13934  }
13935  (opcode>'y').move_to(code);
13936  _cimg_mp_return(pos);
13937  }
13938  if (!std::strncmp(ss,"arg(",4)) {
13939  CImgList<uintT> opcode;
13940  if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13941  const unsigned int pos = mempos++;
13942  CImg<uintT>::vector(69,pos).move_to(opcode);
13943  for (char *s = ss4; s<se; ++s) {
13944  char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
13945  CImg<uintT>::vector(compile(s,ns)).move_to(opcode);
13946  s = ns;
13947  }
13948  (opcode>'y').move_to(code);
13949  _cimg_mp_return(pos);
13950  }
13951  if (!std::strncmp(ss,"narg(",5)) {
13952  if (*ss5==')') _cimg_mp_return(0);
13953  unsigned int nb_args = 0;
13954  for (char *s = ss5; s<se; ++s) {
13955  char *ns = s; while (ns<se && (*ns!=',' || level[ns-expr._data]!=clevel1) && (*ns!=')' || level[ns-expr._data]!=clevel)) ++ns;
13956  ++nb_args; s = ns;
13957  }
13958  if (nb_args==0 || nb_args==1) _cimg_mp_return(nb_args);
13959  if (mempos>=mem.size()) mem.resize(-200,1,1,1,0);
13960  const unsigned int pos = mempos++;
13961  mem[pos] = nb_args;
13962  _cimg_mp_return(pos);
13963  }
13964  if (!std::strncmp(ss,"isval(",6)) {
13965  char sep = 0, end = 0; double val = 0;
13966  if (std::sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
13967  _cimg_mp_return(0);
13968  }
13969  if (!std::strncmp(ss,"isnan(",6)) _cimg_mp_opcode1(50,compile(ss6,se1));
13970  if (!std::strncmp(ss,"isinf(",6)) _cimg_mp_opcode1(51,compile(ss6,se1));
13971  if (!std::strncmp(ss,"isint(",6)) _cimg_mp_opcode1(52,compile(ss6,se1));
13972  if (!std::strncmp(ss,"isbool(",7)) _cimg_mp_opcode1(53,compile(ss7,se1));
13973  if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) {
13974  unsigned int value = 0, nb = 1;
13975  char *s1 = ss4; while (s1<se2 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
13976  value = compile(ss4,s1==se2?++s1:s1);
13977  if (s1<se1) {
13978  char *s2 = s1+1; while (s2<se2 && (*s2!=',' || level[s2-expr._data]!=clevel1)) ++s2;
13979  nb = compile(s1+1,se1);
13980  }
13981  _cimg_mp_opcode2(*ss2=='l'?54:55,value,nb);
13982  }
13983 
13984  if (!std::strncmp(ss,"sinc(",5)) _cimg_mp_opcode1(56,compile(ss5,se1));
13985  if (!std::strncmp(ss,"int(",4)) _cimg_mp_opcode1(70,compile(ss4,se1));
13986  }
13987 
13988  // No known item found, assuming this is an already initialized variable.
13989  CImg<charT> variable_name(ss,(unsigned int)(se-ss+1));
13990  variable_name.back() = 0;
13991  for (unsigned int i = 0; i<mempos; ++i) if (label[i]._data && !std::strcmp(variable_name,label[i])) _cimg_mp_return(i);
13992  *se = saved_char;
13993  throw CImgArgumentException("[_cimg_math_parser] "
13994  "CImg<%s>::%s(): Invalid item '%s' in specified expression '%s'.\n",
13995  pixel_type(),calling_function,
13996  variable_name._data,expr._data);
13997  return 0;
13998  }
13999 
14000  // Evaluation functions, known by the parser.
14001  double mp_u() {
14002  return mem[opcode(2)] + cimg::rand()*(mem[opcode(3)]-mem[opcode(2)]);
14003  }
14004  double mp_g() {
14005  return cimg::grand();
14006  }
14007  double mp_i() {
14008  return (double)reference.atXYZC((int)mem[8],(int)mem[9],(int)mem[10],(int)mem[11],0);
14009  }
14010  double mp_xw() {
14011  return mem[8]/reference.width();
14012  }
14013  double mp_yh() {
14014  return mem[9]/reference.height();
14015  }
14016  double mp_zd() {
14017  return mem[10]/reference.depth();
14018  }
14019  double mp_cs() {
14020  return mem[11]/reference.spectrum();
14021  }
14022  double mp_equal() {
14023  return mem[opcode[2]];
14024  }
14025  double mp_logical_and() {
14026  return (double)((bool)mem[opcode(2)] && (bool)mem[opcode(3)]);
14027  }
14028  double mp_logical_or() {
14029  return (double)((bool)mem[opcode(2)] || (bool)mem[opcode(3)]);
14030  }
14031  double mp_infeq() {
14032  return (double)(mem[opcode(2)]<=mem[opcode(3)]);
14033  }
14034  double mp_supeq() {
14035  return (double)(mem[opcode(2)]>=mem[opcode(3)]);
14036  }
14037  double mp_noteq() {
14038  return (double)(mem[opcode(2)]!=mem[opcode(3)]);
14039  }
14040  double mp_eqeq() {
14041  return (double)(mem[opcode(2)]==mem[opcode(3)]);
14042  }
14043  double mp_inf() {
14044  return (double)(mem[opcode(2)]<mem[opcode(3)]);
14045  }
14046  double mp_sup() {
14047  return (double)(mem[opcode(2)]>mem[opcode(3)]);
14048  }
14049  double mp_add() {
14050  return mem[opcode(2)] + mem[opcode(3)];
14051  }
14052  double mp_sub() {
14053  return mem[opcode(2)] - mem[opcode(3)];
14054  }
14055  double mp_mul() {
14056  return mem[opcode(2)] * mem[opcode(3)];
14057  }
14058  double mp_div() {
14059  return mem[opcode(2)] / mem[opcode(3)];
14060  }
14061  double mp_minus() {
14062  return -mem[opcode(2)];
14063  }
14064  double mp_not() {
14065  return !mem[opcode(2)];
14066  }
14067  double mp_logical_not() {
14068  return !mem[opcode(2)];
14069  }
14070  double mp_bitwise_not() {
14071  return ~(unsigned long)mem[opcode(2)];
14072  }
14073  double mp_modulo() {
14074  return cimg::mod(mem[opcode(2)],mem[opcode(3)]);
14075  }
14076  double mp_bitwise_and() {
14077  return ((unsigned long)mem[opcode(2)] & (unsigned long)mem[opcode(3)]);
14078  }
14079  double mp_bitwise_or() {
14080  return ((unsigned long)mem[opcode(2)] | (unsigned long)mem[opcode(3)]);
14081  }
14082  double mp_pow() {
14083  return std::pow(mem[opcode(2)],mem[opcode(3)]);
14084  }
14085  double mp_sin() {
14086  return std::sin(mem[opcode(2)]);
14087  }
14088  double mp_cos() {
14089  return std::cos(mem[opcode(2)]);
14090  }
14091  double mp_tan() {
14092  return std::tan(mem[opcode(2)]);
14093  }
14094  double mp_asin() {
14095  return std::asin(mem[opcode(2)]);
14096  }
14097  double mp_acos() {
14098  return std::acos(mem[opcode(2)]);
14099  }
14100  double mp_atan() {
14101  return std::atan(mem[opcode(2)]);
14102  }
14103  double mp_sinh() {
14104  return std::sinh(mem[opcode(2)]);
14105  }
14106  double mp_cosh() {
14107  return std::cosh(mem[opcode(2)]);
14108  }
14109  double mp_tanh() {
14110  return std::tanh(mem[opcode(2)]);
14111  }
14112  double mp_log10() {
14113  return std::log10(mem[opcode(2)]);
14114  }
14115  double mp_log2() {
14116  return cimg::log2(mem[opcode(2)]);
14117  }
14118  double mp_log() {
14119  return std::log(mem[opcode(2)]);
14120  }
14121  double mp_exp() {
14122  return std::exp(mem[opcode(2)]);
14123  }
14124  double mp_sqrt() {
14125  return std::sqrt(mem[opcode(2)]);
14126  }
14127  double mp_sign() {
14128  return cimg::sign(mem[opcode(2)]);
14129  }
14130  double mp_abs() {
14131  return cimg::abs(mem[opcode(2)]);
14132  }
14133  double mp_atan2() {
14134  return std::atan2(mem[opcode(2)],mem[opcode(3)]);
14135  }
14136  double mp_if() {
14137  return mem[opcode(2)]?mem[opcode(3)]:mem[opcode(4)];
14138  }
14139  double mp_round() {
14140  return cimg::round(mem[opcode(2)],mem[opcode(3)],(int)mem[opcode(4)]);
14141  }
14142  double mp_ixyzc() {
14143  const int i = (int)mem[opcode(6)], b = (int)mem[opcode(7)];
14144  if (i==0) { // Nearest neighbor interpolation.
14145  if (b==2) return (double)reference.atXYZC(cimg::mod((int)mem[opcode(2)],reference.width()),
14146  cimg::mod((int)mem[opcode(3)],reference.height()),
14147  cimg::mod((int)mem[opcode(4)],reference.depth()),
14148  cimg::mod((int)mem[opcode(5)],reference.spectrum()));
14149  if (b==1) return (double)reference.atXYZC((int)mem[opcode(2)],
14150  (int)mem[opcode(3)],
14151  (int)mem[opcode(4)],
14152  (int)mem[opcode(5)]);
14153  return (double)reference.atXYZC((int)mem[opcode(2)],
14154  (int)mem[opcode(3)],
14155  (int)mem[opcode(4)],
14156  (int)mem[opcode(5)],0);
14157  } else { // Linear interpolation.
14158  if (b==2) return (double)reference.linear_atXYZC(cimg::mod((float)mem[opcode(2)],(float)reference.width()),
14159  cimg::mod((float)mem[opcode(3)],(float)reference.height()),
14160  cimg::mod((float)mem[opcode(4)],(float)reference.depth()),
14161  cimg::mod((float)mem[opcode(5)],(float)reference.spectrum()));
14162  if (b==1) return (double)reference.linear_atXYZC((float)mem[opcode(2)],
14163  (float)mem[opcode(3)],
14164  (float)mem[opcode(4)],
14165  (float)mem[opcode(5)]);
14166  return (double)reference.linear_atXYZC((float)mem[opcode(2)],
14167  (float)mem[opcode(3)],
14168  (float)mem[opcode(4)],
14169  (float)mem[opcode(5)],0);
14170  }
14171  }
14172  double mp_min() {
14173  double val = mem[opcode(2)];
14174  for (unsigned int i = 3; i<opcode._height; ++i) val = cimg::min(val,mem[opcode(i)]);
14175  return val;
14176  }
14177  double mp_max() {
14178  double val = mem[opcode(2)];
14179  for (unsigned int i = 3; i<opcode._height; ++i) val = cimg::max(val,mem[opcode(i)]);
14180  return val;
14181  }
14182  double mp_isnan() {
14183  const double val = mem[opcode(2)];
14184  return !(val==val);
14185  }
14186  double mp_isinf() {
14187  const double val = mem[opcode(2)];
14188  return val==(val+1);
14189  }
14190  double mp_isint() {
14191  const double val = mem[opcode(2)];
14192  return (double)(cimg::mod(val,1.0)==0);
14193  }
14194  double mp_isbool() {
14195  const double val = mem[opcode(2)];
14196  return (val==0.0 || val==1.0);
14197  }
14198  double mp_rol() {
14199  return cimg::rol(mem[opcode(2)],(unsigned int)mem[opcode(3)]);
14200  }
14201  double mp_ror() {
14202  return cimg::ror(mem[opcode(2)],(unsigned int)mem[opcode(3)]);
14203  }
14204  double mp_lsl() {
14205  return (long)mem[opcode(2)]<<(unsigned int)mem[opcode(3)];
14206  }
14207  double mp_lsr() {
14208  return (long)mem[opcode(2)]>>(unsigned int)mem[opcode(3)];
14209  }
14210  double mp_sinc() {
14211  return cimg::sinc(mem[opcode(2)]);
14212  }
14213  double mp_im() {
14214  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14215  return reference_stats?reference_stats[0]:0;
14216  }
14217  double mp_iM() {
14218  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14219  return reference_stats?reference_stats[1]:0;
14220  }
14221  double mp_ia() {
14222  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14223  return reference_stats?reference_stats[2]:0;
14224  }
14225  double mp_iv() {
14226  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14227  return reference_stats?reference_stats[3]:0;
14228  }
14229  double mp_xm() {
14230  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14231  return reference_stats?reference_stats[4]:0;
14232  }
14233  double mp_ym() {
14234  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14235  return reference_stats?reference_stats[5]:0;
14236  }
14237  double mp_zm() {
14238  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14239  return reference_stats?reference_stats[6]:0;
14240  }
14241  double mp_cm() {
14242  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14243  return reference_stats?reference_stats[7]:0;
14244  }
14245  double mp_xM() {
14246  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14247  return reference_stats?reference_stats[8]:0;
14248  }
14249  double mp_yM() {
14250  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14251  return reference_stats?reference_stats[9]:0;
14252  }
14253  double mp_zM() {
14254  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14255  return reference_stats?reference_stats[10]:0;
14256  }
14257  double mp_cM() {
14258  if (!reference_stats) reference.get_stats().move_to(reference_stats);
14259  return reference_stats?reference_stats[11]:0;
14260  }
14261  double mp_arg() {
14262  const int _ind = (int)mem[opcode(2)];
14263  const unsigned int nb_args = opcode._height-2, ind = _ind<0?_ind+nb_args:(unsigned int)_ind;
14264  if (ind>=nb_args) return 0;
14265  return mem[opcode(ind+2)];
14266  }
14267  double mp_int() {
14268  return (double)(long)mem[opcode(2)];
14269  }
14270 
14271  // Evaluation procedure, with image data.
14272  double eval(const double x, const double y, const double z, const double c) {
14273  typedef double (_cimg_math_parser::*mp_func)();
14274  const mp_func mp_funcs[] = {
14275  &_cimg_math_parser::mp_u, // 0
14276  &_cimg_math_parser::mp_g, // 1
14277  &_cimg_math_parser::mp_i, // 2
14278  &_cimg_math_parser::mp_xw, // 3
14279  &_cimg_math_parser::mp_yh, // 4
14280  &_cimg_math_parser::mp_zd, // 5
14281  &_cimg_math_parser::mp_cs, // 6
14282  &_cimg_math_parser::mp_equal, // 7
14283  &_cimg_math_parser::mp_logical_or, // 8
14284  &_cimg_math_parser::mp_logical_and, // 9
14285  &_cimg_math_parser::mp_bitwise_or, // 10
14286  &_cimg_math_parser::mp_bitwise_and, // 11
14287  &_cimg_math_parser::mp_noteq, // 12
14288  &_cimg_math_parser::mp_eqeq, // 13
14289  &_cimg_math_parser::mp_infeq, // 14
14290  &_cimg_math_parser::mp_supeq, // 15
14291  &_cimg_math_parser::mp_inf, // 16
14292  &_cimg_math_parser::mp_sup, // 17
14293  &_cimg_math_parser::mp_lsl, // 18
14294  &_cimg_math_parser::mp_lsr, // 19
14295  &_cimg_math_parser::mp_sub, // 20
14296  &_cimg_math_parser::mp_add, // 21
14297  &_cimg_math_parser::mp_mul, // 22
14298  &_cimg_math_parser::mp_div, // 23
14299  &_cimg_math_parser::mp_modulo, // 24
14300  &_cimg_math_parser::mp_pow, // 25
14301  &_cimg_math_parser::mp_minus, // 26
14302  &_cimg_math_parser::mp_logical_not, // 27
14303  &_cimg_math_parser::mp_bitwise_not, // 28
14304  &_cimg_math_parser::mp_sin, // 29
14305  &_cimg_math_parser::mp_cos, // 30
14306  &_cimg_math_parser::mp_tan, // 31
14307  &_cimg_math_parser::mp_asin, // 32
14308  &_cimg_math_parser::mp_acos, // 33
14309  &_cimg_math_parser::mp_atan, // 34
14310  &_cimg_math_parser::mp_sinh, // 35
14311  &_cimg_math_parser::mp_cosh, // 36
14312  &_cimg_math_parser::mp_tanh, // 37
14313  &_cimg_math_parser::mp_log10, // 38
14314  &_cimg_math_parser::mp_log, // 39
14315  &_cimg_math_parser::mp_exp, // 40
14316  &_cimg_math_parser::mp_sqrt, // 41
14317  &_cimg_math_parser::mp_sign, // 42
14318  &_cimg_math_parser::mp_abs, // 43
14319  &_cimg_math_parser::mp_atan2, // 44
14320  &_cimg_math_parser::mp_if, // 45
14321  &_cimg_math_parser::mp_round, // 46
14322  &_cimg_math_parser::mp_ixyzc, // 47
14323  &_cimg_math_parser::mp_min, // 48
14324  &_cimg_math_parser::mp_max, // 49
14325  &_cimg_math_parser::mp_isnan, // 50
14326  &_cimg_math_parser::mp_isinf, // 51
14327  &_cimg_math_parser::mp_isint, // 52
14328  &_cimg_math_parser::mp_isbool, // 53
14329  &_cimg_math_parser::mp_rol, // 54
14330  &_cimg_math_parser::mp_ror, // 55
14331  &_cimg_math_parser::mp_sinc, // 56
14332  &_cimg_math_parser::mp_im, // 57
14333  &_cimg_math_parser::mp_iM, // 58
14334  &_cimg_math_parser::mp_ia, // 59
14335  &_cimg_math_parser::mp_iv, // 60
14336  &_cimg_math_parser::mp_xm, // 61
14337  &_cimg_math_parser::mp_ym, // 62
14338  &_cimg_math_parser::mp_zm, // 63
14339  &_cimg_math_parser::mp_cm, // 64
14340  &_cimg_math_parser::mp_xM, // 65
14341  &_cimg_math_parser::mp_yM, // 66
14342  &_cimg_math_parser::mp_zM, // 67
14343  &_cimg_math_parser::mp_cM, // 68
14344  &_cimg_math_parser::mp_arg, // 69
14345  &_cimg_math_parser::mp_int, // 70
14346  &_cimg_math_parser::mp_log2 // 71
14347  };
14348 
14349  if (!mem) return 0;
14350  mem[8] = x; mem[9] = y; mem[10] = z; mem[11] = c;
14351  opcode._is_shared = true; opcode._width = opcode._depth = opcode._spectrum = 1;
14352  cimglist_for(code,l) {
14353  const CImg<uintT> &op = code[l];
14354  opcode._data = op._data; opcode._height = op._height; // Allows to avoid parameter passing to evaluation functions.
14355  mem[opcode(1)] = (this->*mp_funcs[opcode[0]])();
14356  }
14357  return mem[result];
14358  }
14359  };
14360 
14362 
14375  cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)(val*val); };
14376  return *this;
14377  }
14378 
14381  return CImg<Tfloat>(*this,false).sqr();
14382  }
14383 
14385 
14398  cimg_for(*this,ptrd,T) *ptrd = (T)std::sqrt((double)*ptrd);
14399  return *this;
14400  }
14401 
14404  return CImg<Tfloat>(*this,false).sqrt();
14405  }
14406 
14408 
14415  cimg_for(*this,ptrd,T) *ptrd = (T)std::exp((double)*ptrd);
14416  return *this;
14417  }
14418 
14421  return CImg<Tfloat>(*this,false).exp();
14422  }
14423 
14425 
14432  cimg_for(*this,ptrd,T) *ptrd = (T)std::log((double)*ptrd);
14433  return *this;
14434  }
14435 
14438  return CImg<Tfloat>(*this,false).log();
14439  }
14440 
14442 
14449  cimg_for(*this,ptrd,T) *ptrd = (T)cimg::log2((double)*ptrd);
14450  return *this;
14451  }
14452 
14455  return CImg<Tfloat>(*this,false).log2();
14456  }
14457 
14459 
14466  cimg_for(*this,ptrd,T) *ptrd = (T)std::log10((double)*ptrd);
14467  return *this;
14468  }
14469 
14472  return CImg<Tfloat>(*this,false).log10();
14473  }
14474 
14476 
14483  cimg_for(*this,ptrd,T) *ptrd = cimg::abs(*ptrd);
14484  return *this;
14485  }
14486 
14489  return CImg<Tfloat>(*this,false).abs();
14490  }
14491 
14493 
14504  cimg_for(*this,ptrd,T) *ptrd = cimg::sign(*ptrd);
14505  return *this;
14506  }
14507 
14510  return CImg<Tfloat>(*this,false).sign();
14511  }
14512 
14514 
14522  cimg_for(*this,ptrd,T) *ptrd = (T)std::cos((double)*ptrd);
14523  return *this;
14524  }
14525 
14528  return CImg<Tfloat>(*this,false).cos();
14529  }
14530 
14532 
14540  cimg_for(*this,ptrd,T) *ptrd = (T)std::sin((double)*ptrd);
14541  return *this;
14542  }
14543 
14546  return CImg<Tfloat>(*this,false).sin();
14547  }
14548 
14550 
14558  cimg_for(*this,ptrd,T) *ptrd = (T)cimg::sinc((double)*ptrd);
14559  return *this;
14560  }
14561 
14564  return CImg<Tfloat>(*this,false).sinc();
14565  }
14566 
14568 
14576  cimg_for(*this,ptrd,T) *ptrd = (T)std::tan((double)*ptrd);
14577  return *this;
14578  }
14579 
14582  return CImg<Tfloat>(*this,false).tan();
14583  }
14584 
14586 
14593  cimg_for(*this,ptrd,T) *ptrd = (T)std::cosh((double)*ptrd);
14594  return *this;
14595  }
14596 
14599  return CImg<Tfloat>(*this,false).cosh();
14600  }
14601 
14603 
14610  cimg_for(*this,ptrd,T) *ptrd = (T)std::sinh((double)*ptrd);
14611  return *this;
14612  }
14613 
14616  return CImg<Tfloat>(*this,false).sinh();
14617  }
14618 
14620 
14627  cimg_for(*this,ptrd,T) *ptrd = (T)std::tanh((double)*ptrd);
14628  return *this;
14629  }
14630 
14633  return CImg<Tfloat>(*this,false).tanh();
14634  }
14635 
14637 
14644  cimg_for(*this,ptrd,T) *ptrd = (T)std::acos((double)*ptrd);
14645  return *this;
14646  }
14647 
14650  return CImg<Tfloat>(*this,false).acos();
14651  }
14652 
14654 
14661  cimg_for(*this,ptrd,T) *ptrd = (T)std::asin((double)*ptrd);
14662  return *this;
14663  }
14664 
14667  return CImg<Tfloat>(*this,false).asin();
14668  }
14669 
14671 
14678  cimg_for(*this,ptrd,T) *ptrd = (T)std::atan((double)*ptrd);
14679  return *this;
14680  }
14681 
14684  return CImg<Tfloat>(*this,false).atan();
14685  }
14686 
14688 
14703  template<typename t>
14704  CImg<T>& atan2(const CImg<t>& img) {
14705  const unsigned long siz = size(), isiz = img.size();
14706  if (siz && isiz) {
14707  if (is_overlapped(img)) return atan2(+img);
14708  T *ptrd = _data, *const ptre = _data + siz;
14709  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14710  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
14711  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
14712  }
14713  return *this;
14714  }
14715 
14717  template<typename t>
14718  CImg<Tfloat> get_atan2(const CImg<t>& img) const {
14719  return CImg<Tfloat>(*this,false).atan2(img);
14720  }
14721 
14723 
14738  template<typename t>
14739  CImg<T>& mul(const CImg<t>& img) {
14740  const unsigned long siz = size(), isiz = img.size();
14741  if (siz && isiz) {
14742  if (is_overlapped(img)) return mul(+img);
14743  T *ptrd = _data, *const ptre = _data + siz;
14744  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14745  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
14746  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
14747  }
14748  return *this;
14749  }
14750 
14752  template<typename t>
14753  CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
14754  return CImg<_cimg_Tt>(*this,false).mul(img);
14755  }
14756 
14758 
14761  template<typename t>
14762  CImg<T>& div(const CImg<t>& img) {
14763  const unsigned long siz = size(), isiz = img.size();
14764  if (siz && isiz) {
14765  if (is_overlapped(img)) return div(+img);
14766  T *ptrd = _data, *const ptre = _data + siz;
14767  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14768  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
14769  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
14770  }
14771  return *this;
14772  }
14773 
14775  template<typename t>
14776  CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
14777  return CImg<_cimg_Tt>(*this,false).div(img);
14778  }
14779 
14781 
14796  CImg<T>& pow(const double p) {
14797  if (p==0) return fill(1);
14798  if (p==0.5) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = (T)std::sqrt((double)val); } return *this; }
14799  if (p==1) return *this;
14800  if (p==2) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val; } return *this; }
14801  if (p==3) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val; } return *this; }
14802  if (p==4) { cimg_for(*this,ptrd,T) { const T val = *ptrd; *ptrd = val*val*val*val; } return *this; }
14803  cimg_for(*this,ptrd,T) *ptrd = (T)std::pow((double)*ptrd,p);
14804  return *this;
14805  }
14806 
14808  CImg<Tfloat> get_pow(const double p) const {
14809  return CImg<Tfloat>(*this,false).pow(p);
14810  }
14811 
14813 
14816  CImg<T>& pow(const char *const expression) {
14817  const unsigned int omode = cimg::exception_mode();
14818  cimg::exception_mode() = 0;
14819  try {
14820  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14821  _cimg_math_parser mp(base,expression,"pow");
14822  T *ptrd = _data;
14823  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)std::pow((double)*ptrd,mp.eval(x,y,z,c)); ++ptrd; }
14824  } catch (CImgException&) {
14825  CImg<Tfloat> values(_width,_height,_depth,_spectrum);
14826  try {
14827  values.fill(expression,true);
14828  } catch (CImgException&) {
14829  cimg::exception_mode() = omode;
14830  values.load(expression);
14831  }
14832  pow(values);
14833  }
14834  cimg::exception_mode() = omode;
14835  return *this;
14836  }
14837 
14839  CImg<Tfloat> get_pow(const char *const expression) const {
14840  return CImg<Tfloat>(*this,false).pow(expression);
14841  }
14842 
14844 
14847  template<typename t>
14848  CImg<T>& pow(const CImg<t>& img) {
14849  const unsigned long siz = size(), isiz = img.size();
14850  if (siz && isiz) {
14851  if (is_overlapped(img)) return pow(+img);
14852  T *ptrd = _data, *const ptre = _data + siz;
14853  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14854  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
14855  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
14856  }
14857  return *this;
14858  }
14859 
14861  template<typename t>
14862  CImg<Tfloat> get_pow(const CImg<t>& img) const {
14863  return CImg<Tfloat>(*this,false).pow(img);
14864  }
14865 
14867 
14870  CImg<T>& rol(const unsigned int n=1) {
14871  cimg_for(*this,ptrd,T) *ptrd = (T)cimg::rol(*ptrd,n);
14872  return *this;
14873  }
14874 
14876  CImg<T> get_rol(const unsigned int n=1) const {
14877  return (+*this).rol(n);
14878  }
14879 
14881 
14884  CImg<T>& rol(const char *const expression) {
14885  const unsigned int omode = cimg::exception_mode();
14886  cimg::exception_mode() = 0;
14887  try {
14888  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14889  _cimg_math_parser mp(base,expression,"rol");
14890  T *ptrd = _data;
14891  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::rol(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; }
14892  } catch (CImgException&) {
14893  CImg<Tfloat> values(_width,_height,_depth,_spectrum);
14894  try {
14895  values.fill(expression,true);
14896  } catch (CImgException&) {
14897  cimg::exception_mode() = omode;
14898  values.load(expression);
14899  }
14900  rol(values);
14901  }
14902  cimg::exception_mode() = omode;
14903  return *this;
14904  }
14905 
14907  CImg<T> get_rol(const char *const expression) const {
14908  return (+*this).rol(expression);
14909  }
14910 
14912 
14915  template<typename t>
14916  CImg<T>& rol(const CImg<t>& img) {
14917  const unsigned long siz = size(), isiz = img.size();
14918  if (siz && isiz) {
14919  if (is_overlapped(img)) return rol(+img);
14920  T *ptrd = _data, *const ptre = _data + siz;
14921  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14922  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
14923  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
14924  }
14925  return *this;
14926  }
14927 
14929  template<typename t>
14930  CImg<T> get_rol(const CImg<t>& img) const {
14931  return (+*this).rol(img);
14932  }
14933 
14935 
14938  CImg<T>& ror(const unsigned int n=1) {
14939  cimg_for(*this,ptrd,T) *ptrd = (T)cimg::ror(*ptrd,n);
14940  return *this;
14941  }
14942 
14944  CImg<T> get_ror(const unsigned int n=1) const {
14945  return (+*this).ror(n);
14946  }
14947 
14949 
14952  CImg<T>& ror(const char *const expression) {
14953  const unsigned int omode = cimg::exception_mode();
14954  cimg::exception_mode() = 0;
14955  try {
14956  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
14957  _cimg_math_parser mp(base,expression,"ror");
14958  T *ptrd = _data;
14959  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::ror(*ptrd,(unsigned int)mp.eval(x,y,z,c)); ++ptrd; }
14960  } catch (CImgException&) {
14961  CImg<Tfloat> values(_width,_height,_depth,_spectrum);
14962  try {
14963  values.fill(expression,true);
14964  } catch (CImgException&) {
14965  cimg::exception_mode() = omode;
14966  values.load(expression);
14967  }
14968  ror(values);
14969  }
14970  cimg::exception_mode() = omode;
14971  return *this;
14972  }
14973 
14975  CImg<T> get_ror(const char *const expression) const {
14976  return (+*this).ror(expression);
14977  }
14978 
14980 
14983  template<typename t>
14984  CImg<T>& ror(const CImg<t>& img) {
14985  const unsigned long siz = size(), isiz = img.size();
14986  if (siz && isiz) {
14987  if (is_overlapped(img)) return ror(+img);
14988  T *ptrd = _data, *const ptre = _data + siz;
14989  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
14990  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
14991  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
14992  }
14993  return *this;
14994  }
14995 
14997  template<typename t>
14998  CImg<T> get_ror(const CImg<t>& img) const {
14999  return (+*this).ror(img);
15000  }
15001 
15003 
15007  CImg<T>& min(const T val) {
15008  cimg_for(*this,ptrd,T) *ptrd = cimg::min(*ptrd,val);
15009  return *this;
15010  }
15011 
15013  CImg<T> get_min(const T val) const {
15014  return (+*this).min(val);
15015  }
15016 
15018 
15022  template<typename t>
15023  CImg<T>& min(const CImg<t>& img) {
15024  const unsigned long siz = size(), isiz = img.size();
15025  if (siz && isiz) {
15026  if (is_overlapped(img)) return min(+img);
15027  T *ptrd = _data, *const ptre = _data + siz;
15028  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15029  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::min((T)*(ptrs++),*ptrd);
15030  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::min((T)*(ptrs++),*ptrd);
15031  }
15032  return *this;
15033  }
15034 
15036  template<typename t>
15037  CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
15038  return CImg<_cimg_Tt>(*this,false).min(img);
15039  }
15040 
15042 
15046  CImg<T>& min(const char *const expression) {
15047  const unsigned int omode = cimg::exception_mode();
15048  cimg::exception_mode() = 0;
15049  try {
15050  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
15051  _cimg_math_parser mp(base,expression,"min");
15052  T *ptrd = _data;
15053  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::min(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
15054  } catch (CImgException&) {
15055  CImg<T> values(_width,_height,_depth,_spectrum);
15056  try {
15057  values.fill(expression,true);
15058  } catch (CImgException&) {
15059  cimg::exception_mode() = omode;
15060  values.load(expression);
15061  }
15062  min(values);
15063  }
15064  cimg::exception_mode() = omode;
15065  return *this;
15066  }
15067 
15069  CImg<Tfloat> get_min(const char *const expression) const {
15070  return CImg<Tfloat>(*this,false).min(expression);
15071  }
15072 
15074 
15078  CImg<T>& max(const T val) {
15079  cimg_for(*this,ptrd,T) *ptrd = cimg::max(*ptrd,val);
15080  return *this;
15081  }
15082 
15084  CImg<T> get_max(const T val) const {
15085  return (+*this).max(val);
15086  }
15087 
15089 
15093  template<typename t>
15094  CImg<T>& max(const CImg<t>& img) {
15095  const unsigned long siz = size(), isiz = img.size();
15096  if (siz && isiz) {
15097  if (is_overlapped(img)) return max(+img);
15098  T *ptrd = _data, *const ptre = _data + siz;
15099  if (siz>isiz) for (unsigned long n = siz/isiz; n; --n)
15100  for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd) *ptrd = cimg::max((T)*(ptrs++),*ptrd);
15101  for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::max((T)*(ptrs++),*ptrd);
15102  }
15103  return *this;
15104  }
15105 
15107  template<typename t>
15108  CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
15109  return CImg<_cimg_Tt>(*this,false).max(img);
15110  }
15111 
15113 
15117  CImg<T>& max(const char *const expression) {
15118  const unsigned int omode = cimg::exception_mode();
15119  cimg::exception_mode() = 0;
15120  try {
15121  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
15122  _cimg_math_parser mp(base,expression,"max");
15123  T *ptrd = _data;
15124  cimg_forXYZC(*this,x,y,z,c) { *ptrd = (T)cimg::max(*ptrd,(T)mp.eval(x,y,z,c)); ++ptrd; }
15125  } catch (CImgException&) {
15126  CImg<T> values(_width,_height,_depth,_spectrum);
15127  try {
15128  values.fill(expression,true);
15129  } catch (CImgException&) {
15130  cimg::exception_mode() = omode;
15131  values.load(expression);
15132  }
15133  max(values);
15134  }
15135  cimg::exception_mode() = omode;
15136  return *this;
15137  }
15138 
15140  CImg<Tfloat> get_max(const char *const expression) const {
15141  return CImg<Tfloat>(*this,false).max(expression);
15142  }
15143 
15145 
15147  T& min() {
15148  if (is_empty())
15149  throw CImgInstanceException(_cimg_instance
15150  "min(): Empty instance.",
15151  cimg_instance);
15152  T *ptr_min = _data;
15153  T min_value = *ptr_min;
15154  cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
15155  return *ptr_min;
15156  }
15157 
15159  const T& min() const {
15160  if (is_empty())
15161  throw CImgInstanceException(_cimg_instance
15162  "min(): Empty instance.",
15163  cimg_instance);
15164  const T *ptr_min = _data;
15165  T min_value = *ptr_min;
15166  cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
15167  return *ptr_min;
15168  }
15169 
15171 
15173  T& max() {
15174  if (is_empty())
15175  throw CImgInstanceException(_cimg_instance
15176  "max(): Empty instance.",
15177  cimg_instance);
15178  T *ptr_max = _data;
15179  T max_value = *ptr_max;
15180  cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
15181  return *ptr_max;
15182  }
15183 
15185  const T& max() const {
15186  if (is_empty())
15187  throw CImgInstanceException(_cimg_instance
15188  "max(): Empty instance.",
15189  cimg_instance);
15190  const T *ptr_max = _data;
15191  T max_value = *ptr_max;
15192  cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
15193  return *ptr_max;
15194  }
15195 
15197 
15200  template<typename t>
15201  T& min_max(t& max_val) {
15202  if (is_empty())
15203  throw CImgInstanceException(_cimg_instance
15204  "min_max(): Empty instance.",
15205  cimg_instance);
15206  T *ptr_min = _data;
15207  T min_value = *ptr_min, max_value = min_value;
15208  cimg_for(*this,ptrs,T) {
15209  const T val = *ptrs;
15210  if (val<min_value) { min_value = val; ptr_min = ptrs; }
15211  if (val>max_value) max_value = val;
15212  }
15213  max_val = (t)max_value;
15214  return *ptr_min;
15215  }
15216 
15218  template<typename t>
15219  const T& min_max(t& max_val) const {
15220  if (is_empty())
15221  throw CImgInstanceException(_cimg_instance
15222  "min_max(): Empty instance.",
15223  cimg_instance);
15224  const T *ptr_min = _data;
15225  T min_value = *ptr_min, max_value = min_value;
15226  cimg_for(*this,ptrs,T) {
15227  const T val = *ptrs;
15228  if (val<min_value) { min_value = val; ptr_min = ptrs; }
15229  if (val>max_value) max_value = val;
15230  }
15231  max_val = (t)max_value;
15232  return *ptr_min;
15233  }
15234 
15236 
15239  template<typename t>
15240  T& max_min(t& min_val) {
15241  if (is_empty())
15242  throw CImgInstanceException(_cimg_instance
15243  "max_min(): Empty instance.",
15244  cimg_instance);
15245  T *ptr_max = _data;
15246  T max_value = *ptr_max, min_value = max_value;
15247  cimg_for(*this,ptrs,T) {
15248  const T val = *ptrs;
15249  if (val>max_value) { max_value = val; ptr_max = ptrs; }
15250  if (val<min_value) min_value = val;
15251  }
15252  min_val = (t)min_value;
15253  return *ptr_max;
15254  }
15255 
15257  template<typename t>
15258  const T& max_min(t& min_val) const {
15259  if (is_empty())
15260  throw CImgInstanceException(_cimg_instance
15261  "max_min(): Empty instance.",
15262  cimg_instance);
15263  const T *ptr_max = _data;
15264  T max_value = *ptr_max, min_value = max_value;
15265  cimg_for(*this,ptrs,T) {
15266  const T val = *ptrs;
15267  if (val>max_value) { max_value = val; ptr_max = ptrs; }
15268  if (val<min_value) min_value = val;
15269  }
15270  min_val = (t)min_value;
15271  return *ptr_max;
15272  }
15273 
15275 
15278  T kth_smallest(const unsigned int k) const {
15279  if (is_empty())
15280  throw CImgInstanceException(_cimg_instance
15281  "kth_smallest(): Empty instance.",
15282  cimg_instance);
15283  CImg<T> arr(*this);
15284  unsigned int l = 0, ir = size() - 1;
15285  for (;;) {
15286  if (ir<=l+1) {
15287  if (ir==l+1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
15288  return arr[k];
15289  } else {
15290  const unsigned int mid = (l + ir)>>1;
15291  cimg::swap(arr[mid],arr[l+1]);
15292  if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
15293  if (arr[l+1]>arr[ir]) cimg::swap(arr[l+1],arr[ir]);
15294  if (arr[l]>arr[l+1]) cimg::swap(arr[l],arr[l+1]);
15295  unsigned int i = l + 1, j = ir;
15296  const T pivot = arr[l+1];
15297  for (;;) {
15298  do ++i; while (arr[i]<pivot);
15299  do --j; while (arr[j]>pivot);
15300  if (j<i) break;
15301  cimg::swap(arr[i],arr[j]);
15302  }
15303  arr[l+1] = arr[j];
15304  arr[j] = pivot;
15305  if (j>=k) ir = j - 1;
15306  if (j<=k) l = i;
15307  }
15308  }
15309  return 0;
15310  }
15311 
15313 
15315  T median() const {
15316  if (is_empty())
15317  throw CImgInstanceException(_cimg_instance
15318  "median(): Empty instance.",
15319  cimg_instance);
15320  const unsigned int s = size();
15321  const T res = kth_smallest(s>>1);
15322  return (s%2)?res:((res+kth_smallest((s>>1)-1))/2);
15323  }
15324 
15326 
15328  Tdouble sum() const {
15329  if (is_empty())
15330  throw CImgInstanceException(_cimg_instance
15331  "sum(): Empty instance.",
15332  cimg_instance);
15333  Tdouble res = 0;
15334  cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
15335  return res;
15336  }
15337 
15339 
15341  Tdouble mean() const {
15342  if (is_empty())
15343  throw CImgInstanceException(_cimg_instance
15344  "mean(): Empty instance.",
15345  cimg_instance);
15346  Tdouble res = 0;
15347  cimg_for(*this,ptrs,T) res+=(Tdouble)*ptrs;
15348  return res/size();
15349  }
15350 
15352 
15361  Tdouble variance(const unsigned int variance_method=1) const {
15362  Tdouble foo;
15363  return variance_mean(variance_method,foo);
15364  }
15365 
15367 
15371  template<typename t>
15372  Tdouble variance_mean(const unsigned int variance_method, t& mean) const {
15373  if (is_empty())
15374  throw CImgInstanceException(_cimg_instance
15375  "variance_mean(): Empty instance.",
15376  cimg_instance);
15377 
15378  Tdouble variance = 0, average = 0;
15379  const unsigned long siz = size();
15380  switch (variance_method) {
15381  case 0 :{ // Least mean square (standard definition)
15382  Tdouble S = 0, S2 = 0;
15383  cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
15384  variance = (S2 - S*S/siz)/siz;
15385  average = S;
15386  } break;
15387  case 1 : { // Least mean square (robust definition)
15388  Tdouble S = 0, S2 = 0;
15389  cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)*ptrs; S+=val; S2+=val*val; }
15390  variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
15391  average = S;
15392  } break;
15393  case 2 : { // Least Median of Squares (MAD)
15394  CImg<Tfloat> buf(*this);
15395  buf.sort();
15396  const unsigned long siz2 = siz>>1;
15397  const Tdouble med_i = (double)buf[siz2];
15398  cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val; }
15399  buf.sort();
15400  const Tdouble sig = (Tdouble)(1.4828*buf[siz2]);
15401  variance = sig*sig;
15402  } break;
15403  default : { // Least trimmed of Squares
15404  CImg<Tfloat> buf(*this);
15405  const unsigned long siz2 = siz>>1;
15406  cimg_for(buf,ptrs,Tfloat) { const Tdouble val = (Tdouble)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val; }
15407  buf.sort();
15408  Tdouble a = 0;
15409  const Tfloat *ptrs = buf._data;
15410  for (unsigned long j = 0; j<siz2; ++j) a+=(Tdouble)*(ptrs++);
15411  const Tdouble sig = (Tdouble)(2.6477*std::sqrt(a/siz2));
15412  variance = sig*sig;
15413  }
15414  }
15415  mean = (t)(average/siz);
15416  return variance>0?variance:0;
15417  }
15418 
15420 
15428  Tdouble variance_noise(const unsigned int variance_method=2) const {
15429  if (is_empty())
15430  throw CImgInstanceException(_cimg_instance
15431  "variance_noise(): Empty instance.",
15432  cimg_instance);
15433 
15434  const unsigned long siz = size();
15435  if (!siz || !_data) return 0;
15436  if (variance_method>1) { // Compute a scaled version of the Laplacian.
15437  CImg<Tdouble> tmp(*this);
15438  if (_depth==1) {
15439  const Tdouble cste = 1.0/std::sqrt(20.0); // Depends on how the Laplacian is computed.
15440  CImg_3x3(I,T);
15441  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
15442  tmp(x,y,c) = cste*((Tdouble)Inc + (Tdouble)Ipc + (Tdouble)Icn +
15443  (Tdouble)Icp - 4*(Tdouble)Icc);
15444  }
15445  } else {
15446  const Tdouble cste = 1.0/std::sqrt(42.0); // Depends on how the Laplacian is computed.
15447  CImg_3x3x3(I,T);
15448  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
15449  tmp(x,y,z,c) = cste*(
15450  (Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc + (Tdouble)Icpc +
15451  (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc);
15452  }
15453  }
15454  return tmp.variance(variance_method);
15455  }
15456 
15457  // Version that doesn't need intermediate images.
15458  Tdouble variance = 0, S = 0, S2 = 0;
15459  if (_depth==1) {
15460  const Tdouble cste = 1.0/std::sqrt(20.0);
15461  CImg_3x3(I,T);
15462  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
15463  const Tdouble val = cste*((Tdouble)Inc + (Tdouble)Ipc +
15464  (Tdouble)Icn + (Tdouble)Icp - 4*(Tdouble)Icc);
15465  S+=val; S2+=val*val;
15466  }
15467  } else {
15468  const Tdouble cste = 1.0/std::sqrt(42.0);
15469  CImg_3x3x3(I,T);
15470  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
15471  const Tdouble val = cste *
15472  ((Tdouble)Incc + (Tdouble)Ipcc + (Tdouble)Icnc +
15473  (Tdouble)Icpc +
15474  (Tdouble)Iccn + (Tdouble)Iccp - 6*(Tdouble)Iccc);
15475  S+=val; S2+=val*val;
15476  }
15477  }
15478  if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
15479  else variance = (S2 - S*S/siz)/siz;
15480  return variance>0?variance:0;
15481  }
15482 
15484 
15487  template<typename t>
15488  Tdouble MSE(const CImg<t>& img) const {
15489  if (img.size()!=size())
15490  throw CImgArgumentException(_cimg_instance
15491  "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
15492  cimg_instance,
15493  img._width,img._height,img._depth,img._spectrum,img._data);
15494  Tdouble vMSE = 0;
15495  const t* ptr2 = img._data;
15496  cimg_for(*this,ptr1,T) {
15497  const Tdouble diff = (Tdouble)*ptr1 - (Tdouble)*(ptr2++);
15498  vMSE+=diff*diff;
15499  }
15500  const unsigned long siz = img.size();
15501  if (siz) vMSE/=siz;
15502  return vMSE;
15503  }
15504 
15506 
15510  template<typename t>
15511  Tdouble PSNR(const CImg<t>& img, const Tdouble max_value=255) const {
15512  const Tdouble vMSE = (Tdouble)std::sqrt(MSE(img));
15513  return (vMSE!=0)?(Tdouble)(20*std::log10(max_value/vMSE)):(Tdouble)(cimg::type<Tdouble>::max());
15514  }
15515 
15517 
15525  double eval(const char *const expression, const double x=0, const double y=0, const double z=0, const double c=0) const {
15526  static _cimg_math_parser *mp = 0;
15527  if (expression) { delete mp; mp = 0; mp = new _cimg_math_parser(*this,expression,"eval"); }
15528  return mp?mp->eval(x,y,z,c):0;
15529  }
15530 
15532 
15536  CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
15537  if (is_empty()) return CImg<doubleT>();
15538  const unsigned long siz = size();
15539  const T *const odata = _data;
15540  const T *pm = odata, *pM = odata;
15541  Tdouble S = 0, S2 = 0;
15542  T m = *pm, M = m;
15543  cimg_for(*this,ptrs,T) {
15544  const T val = *ptrs;
15545  const Tdouble _val = (Tdouble)val;
15546  if (val<m) { m = val; pm = ptrs; }
15547  if (val>M) { M = val; pM = ptrs; }
15548  S+=_val;
15549  S2+=_val*_val;
15550  }
15551  const Tdouble
15552  mean_value = S/siz,
15553  _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
15554  (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
15555  variance(variance_method)),
15556  variance_value = _variance_value>0?_variance_value:0;
15557  int
15558  xm = 0, ym = 0, zm = 0, cm = 0,
15559  xM = 0, yM = 0, zM = 0, cM = 0;
15560  contains(*pm,xm,ym,zm,cm);
15561  contains(*pM,xM,yM,zM,cM);
15562  return CImg<Tdouble>(1,12).fill((Tdouble)m,(Tdouble)M,mean_value,variance_value,
15563  (Tdouble)xm,(Tdouble)ym,(Tdouble)zm,(Tdouble)cm,
15564  (Tdouble)xM,(Tdouble)yM,(Tdouble)zM,(Tdouble)cM);
15565  }
15566 
15568  CImg<T>& stats(const unsigned int variance_method=1) {
15569  return get_stats(variance_method).move_to(*this);
15570  }
15571 
15573  //-------------------------------------
15574  //
15576 
15577  //-------------------------------------
15578 
15580 
15586  Tdouble magnitude(const int magnitude_type=2) const {
15587  if (is_empty())
15588  throw CImgInstanceException(_cimg_instance
15589  "magnitude(): Empty instance.",
15590  cimg_instance);
15591  Tdouble res = 0;
15592  switch (magnitude_type) {
15593  case -1 : {
15594  cimg_for(*this,ptrs,T) { const Tdouble val = (Tdouble)cimg::abs(*ptrs); if (val>res) res = val; }
15595  } break;
15596  case 1 : {
15597  cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::abs(*ptrs);
15598  } break;
15599  default : {
15600  cimg_for(*this,ptrs,T) res+=(Tdouble)cimg::sqr(*ptrs);
15601  res = (Tdouble)std::sqrt(res);
15602  }
15603  }
15604  return res;
15605  }
15606 
15608 
15610  Tdouble trace() const {
15611  if (is_empty())
15612  throw CImgInstanceException(_cimg_instance
15613  "trace(): Empty instance.",
15614  cimg_instance);
15615  Tdouble res = 0;
15616  cimg_forX(*this,k) res+=(Tdouble)(*this)(k,k);
15617  return res;
15618  }
15619 
15621 
15623  Tdouble det() const {
15624  if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
15625  throw CImgInstanceException(_cimg_instance
15626  "det(): Instance is not a square matrix.",
15627  cimg_instance);
15628 
15629  switch (_width) {
15630  case 1 : return (Tdouble)((*this)(0,0));
15631  case 2 : return (Tdouble)((*this)(0,0))*(Tdouble)((*this)(1,1)) - (Tdouble)((*this)(0,1))*(Tdouble)((*this)(1,0));
15632  case 3 : {
15633  const Tdouble
15634  a = (Tdouble)_data[0], d = (Tdouble)_data[1], g = (Tdouble)_data[2],
15635  b = (Tdouble)_data[3], e = (Tdouble)_data[4], h = (Tdouble)_data[5],
15636  c = (Tdouble)_data[6], f = (Tdouble)_data[7], i = (Tdouble)_data[8];
15637  return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
15638  }
15639  default : {
15640  CImg<Tfloat> lu(*this);
15641  CImg<uintT> indx;
15642  bool d;
15643  lu._LU(indx,d);
15644  Tdouble res = d?(Tdouble)1:(Tdouble)-1;
15645  cimg_forX(lu,i) res*=lu(i,i);
15646  return res;
15647  }
15648  }
15649  return 0;
15650  }
15651 
15653 
15656  template<typename t>
15657  Tdouble dot(const CImg<t>& img) const {
15658  if (is_empty())
15659  throw CImgInstanceException(_cimg_instance
15660  "dot(): Empty instance.",
15661  cimg_instance);
15662  if (!img)
15663  throw CImgArgumentException(_cimg_instance
15664  "dot(): Empty specified image.",
15665  cimg_instance);
15666 
15667  const unsigned int nb = cimg::min(size(),img.size());
15668  Tdouble res = 0;
15669  for (unsigned int off = 0; off<nb; ++off) res+=(Tdouble)_data[off]*(Tdouble)img[off];
15670  return res;
15671  }
15672 
15674 
15679  CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
15680  _cimg_static CImg<T> res;
15681  if (res._height!=_spectrum) res.assign(1,_spectrum);
15682  const unsigned long whd = (unsigned long)_width*_height*_depth;
15683  const T *ptrs = data(x,y,z);
15684  T *ptrd = res._data;
15685  cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
15686  return res;
15687  }
15688 
15690 
15696  CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
15697  const int n = (int)std::sqrt((double)_spectrum);
15698  const T *ptrs = data(x,y,z,0);
15699  const unsigned long whd = (unsigned long)_width*_height*_depth;
15700  CImg<T> res(n,n);
15701  T *ptrd = res._data;
15702  cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
15703  return res;
15704  }
15705 
15707 
15712  CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
15713  const T *ptrs = data(x,y,z,0);
15714  const unsigned long whd = (unsigned long)_width*_height*_depth;
15715  if (_spectrum==6) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd),*(ptrs+3*whd),*(ptrs+4*whd),*(ptrs+5*whd));
15716  if (_spectrum==3) return tensor(*ptrs,*(ptrs+whd),*(ptrs+2*whd));
15717  return tensor(*ptrs);
15718  }
15719 
15721 
15727  template<typename t>
15728  CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
15729  if (x<_width && y<_height && z<_depth) {
15730  const t *ptrs = vec._data;
15731  const unsigned long whd = (unsigned long)_width*_height*_depth;
15732  T *ptrd = data(x,y,z);
15733  for (unsigned int k = cimg::min((unsigned int)vec.size(),_spectrum); k; --k) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
15734  }
15735  return *this;
15736  }
15737 
15739 
15745  template<typename t>
15746  CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
15747  return set_vector_at(mat,x,y,z);
15748  }
15749 
15751 
15757  template<typename t>
15758  CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
15759  T *ptrd = data(x,y,z,0);
15760  const unsigned long siz = (unsigned long)_width*_height*_depth;
15761  if (ten._height==2) {
15762  *ptrd = (T)ten[0]; ptrd+=siz;
15763  *ptrd = (T)ten[1]; ptrd+=siz;
15764  *ptrd = (T)ten[3];
15765  }
15766  else {
15767  *ptrd = (T)ten[0]; ptrd+=siz;
15768  *ptrd = (T)ten[1]; ptrd+=siz;
15769  *ptrd = (T)ten[2]; ptrd+=siz;
15770  *ptrd = (T)ten[4]; ptrd+=siz;
15771  *ptrd = (T)ten[5]; ptrd+=siz;
15772  *ptrd = (T)ten[8];
15773  }
15774  return *this;
15775  }
15776 
15778 
15782  return unroll('y');
15783  }
15784 
15787  return get_unroll('y');
15788  }
15789 
15791 
15794  const unsigned long siz = size();
15795  switch (siz) {
15796  case 1 : break;
15797  case 4 : _width = _height = 2; break;
15798  case 9 : _width = _height = 3; break;
15799  case 16 : _width = _height = 4; break;
15800  case 25 : _width = _height = 5; break;
15801  case 36 : _width = _height = 6; break;
15802  case 49 : _width = _height = 7; break;
15803  case 64 : _width = _height = 8; break;
15804  case 81 : _width = _height = 9; break;
15805  case 100 : _width = _height = 10; break;
15806  default : {
15807  unsigned long i = 11, i2 = i*i;
15808  while (i2<siz) { i2+=2*i + 1; ++i; }
15809  if (i2==siz) _width = _height = i;
15810  else throw CImgInstanceException(_cimg_instance
15811  "matrix(): Invalid instance size %u (should be a square integer).",
15812  cimg_instance,
15813  siz);
15814  }
15815  }
15816  return *this;
15817  }
15818 
15821  return (+*this).matrix();
15822  }
15823 
15825 
15828  return get_tensor().move_to(*this);
15829  }
15830 
15833  CImg<T> res;
15834  const unsigned long siz = size();
15835  switch (siz) {
15836  case 1 : break;
15837  case 3 :
15838  res.assign(2,2);
15839  res(0,0) = (*this)(0);
15840  res(1,0) = res(0,1) = (*this)(1);
15841  res(1,1) = (*this)(2);
15842  break;
15843  case 6 :
15844  res.assign(3,3);
15845  res(0,0) = (*this)(0);
15846  res(1,0) = res(0,1) = (*this)(1);
15847  res(2,0) = res(0,2) = (*this)(2);
15848  res(1,1) = (*this)(3);
15849  res(2,1) = res(1,2) = (*this)(4);
15850  res(2,2) = (*this)(5);
15851  break;
15852  default :
15853  throw CImgInstanceException(_cimg_instance
15854  "tensor(): Invalid instance size (does not define a 1x1, 2x2 or 3x3 tensor).",
15855  cimg_instance);
15856  }
15857  return res;
15858  }
15859 
15861 
15865  return get_diagonal().move_to(*this);
15866  }
15867 
15870  if (is_empty()) return *this;
15871  CImg<T> res(size(),size(),1,1,0);
15872  cimg_foroff(*this,off) res(off,off) = (*this)(off);
15873  return res;
15874  }
15875 
15877 
15881  return identity_matrix(cimg::max(_width,_height)).move_to(*this);
15882  }
15883 
15886  return identity_matrix(cimg::max(_width,_height));
15887  }
15888 
15890 
15894  CImg<T>& sequence(const T a0, const T a1) {
15895  if (is_empty()) return *this;
15896  const unsigned int siz = size() - 1;
15897  T* ptr = _data;
15898  if (siz) {
15899  const Tdouble delta = (Tdouble)a1 - (Tdouble)a0;
15900  cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
15901  } else *ptr = a0;
15902  return *this;
15903  }
15904 
15906  CImg<T> get_sequence(const T a0, const T a1) const {
15907  return (+*this).sequence(a0,a1);
15908  }
15909 
15911 
15915  if (_width==1) { _width = _height; _height = 1; return *this; }
15916  if (_height==1) { _height = _width; _width = 1; return *this; }
15917  if (_width==_height) {
15918  cimg_forYZC(*this,y,z,c) for (int x = y; x<width(); ++x) cimg::swap((*this)(x,y,z,c),(*this)(y,x,z,c));
15919  return *this;
15920  }
15921  return get_transpose().move_to(*this);
15922  }
15923 
15926  return get_permute_axes("yxzc");
15927  }
15928 
15930 
15934  template<typename t>
15935  CImg<T>& cross(const CImg<t>& img) {
15936  if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
15937  throw CImgInstanceException(_cimg_instance
15938  "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3d vectors.",
15939  cimg_instance,
15940  img._width,img._height,img._depth,img._spectrum,img._data);
15941 
15942  const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
15943  (*this)[0] = (T)(y*img[2] - z*img[1]);
15944  (*this)[1] = (T)(z*img[0] - x*img[2]);
15945  (*this)[2] = (T)(x*img[1] - y*img[0]);
15946  return *this;
15947  }
15948 
15950  template<typename t>
15951  CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
15952  return CImg<_cimg_Tt>(*this).cross(img);
15953  }
15954 
15956 
15961  CImg<T>& invert(const bool use_LU=true) {
15962  if (_width!=_height || _depth!=1 || _spectrum!=1)
15963  throw CImgInstanceException(_cimg_instance
15964  "invert(): Instance is not a square matrix.",
15965  cimg_instance);
15966 #ifdef cimg_use_lapack
15967  int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
15968  Tfloat
15969  *const lapA = new Tfloat[N*N],
15970  *const WORK = new Tfloat[LWORK];
15971  cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
15972  cimg::getrf(N,lapA,IPIV,INFO);
15973  if (INFO)
15974  cimg::warn(_cimg_instance
15975  "invert(): LAPACK function dgetrf_() returned error code %d.",
15976  cimg_instance,
15977  INFO);
15978  else {
15979  cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
15980  if (INFO)
15981  cimg::warn(_cimg_instance
15982  "invert(): LAPACK function dgetri_() returned error code %d.",
15983  cimg_instance,
15984  INFO);
15985  }
15986  if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N+l]); else fill(0);
15987  delete[] IPIV; delete[] lapA; delete[] WORK;
15988 #else
15989  const double dete = _width>3?-1.0:det();
15990  if (dete!=0.0 && _width==2) {
15991  const double
15992  a = _data[0], c = _data[1],
15993  b = _data[2], d = _data[3];
15994  _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
15995  _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
15996  } else if (dete!=0.0 && _width==3) {
15997  const double
15998  a = _data[0], d = _data[1], g = _data[2],
15999  b = _data[3], e = _data[4], h = _data[5],
16000  c = _data[6], f = _data[7], i = _data[8];
16001  _data[0] = (T)((i*e-f*h)/dete), _data[1] = (T)((g*f-i*d)/dete), _data[2] = (T)((d*h-g*e)/dete);
16002  _data[3] = (T)((h*c-i*b)/dete), _data[4] = (T)((i*a-c*g)/dete), _data[5] = (T)((g*b-a*h)/dete);
16003  _data[6] = (T)((b*f-e*c)/dete), _data[7] = (T)((d*c-a*f)/dete), _data[8] = (T)((a*e-d*b)/dete);
16004  } else {
16005  if (use_LU) { // LU-based inverse computation
16006  CImg<Tfloat> A(*this), indx, col(1,_width);
16007  bool d;
16008  A._LU(indx,d);
16009  cimg_forX(*this,j) {
16010  col.fill(0);
16011  col(j) = 1;
16012  col._solve(A,indx);
16013  cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
16014  }
16015  } else { // SVD-based inverse computation
16016  CImg<Tfloat> U(_width,_width), S(1,_width), V(_width,_width);
16017  SVD(U,S,V,false);
16018  U.transpose();
16019  cimg_forY(S,k) if (S[k]!=0) S[k]=1/S[k];
16020  S.diagonal();
16021  *this = V*S*U;
16022  }
16023  }
16024 #endif
16025  return *this;
16026  }
16027 
16029  CImg<Tfloat> get_invert(const bool use_LU=true) const {
16030  return CImg<Tfloat>(*this,false).invert(use_LU);
16031  }
16032 
16034 
16037  return get_pseudoinvert().move_to(*this);
16038  }
16039 
16042  CImg<Tfloat> U, S, V;
16043  SVD(U,S,V);
16044  const Tfloat tolerance = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*cimg::max(_width,_height)*S.max();
16045  cimg_forX(V,x) {
16046  const Tfloat s = S(x), invs = s>tolerance?1/s:(Tfloat)0;
16047  cimg_forY(V,y) V(x,y)*=invs;
16048  }
16049  return V*U.transpose();
16050  }
16051 
16053 
16057  template<typename t>
16058  CImg<T>& solve(const CImg<t>& A) {
16059  if (_width!=1 || _depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
16060  throw CImgArgumentException(_cimg_instance
16061  "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have incompatible dimensions.",
16062  cimg_instance,
16063  A._width,A._height,A._depth,A._spectrum,A._data);
16064  typedef _cimg_Ttfloat Ttfloat;
16065  if (A._width==A._height) {
16066 #ifdef cimg_use_lapack
16067  char TRANS = 'N';
16068  int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
16069  Ttfloat
16070  *const lapA = new Ttfloat[N*N],
16071  *const lapB = new Ttfloat[N],
16072  *const WORK = new Ttfloat[LWORK];
16073  cimg_forXY(A,k,l) lapA[k*N+l] = (Ttfloat)(A(k,l));
16074  cimg_forY(*this,i) lapB[i] = (Ttfloat)((*this)(i));
16075  cimg::getrf(N,lapA,IPIV,INFO);
16076  if (INFO)
16077  cimg::warn(_cimg_instance
16078  "solve(): LAPACK library function dgetrf_() returned error code %d.",
16079  cimg_instance,
16080  INFO);
16081 
16082  if (!INFO) {
16083  cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
16084  if (INFO)
16085  cimg::warn(_cimg_instance
16086  "solve(): LAPACK library function dgetrs_() returned error code %d.",
16087  cimg_instance,
16088  INFO);
16089  }
16090  if (!INFO) cimg_forY(*this,i) (*this)(i) = (T)(lapB[i]); else fill(0);
16091  delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
16092 #else
16093  CImg<Ttfloat> lu(A,false);
16094  CImg<Ttfloat> indx;
16095  bool d;
16096  lu._LU(indx,d);
16097  _solve(lu,indx);
16098 #endif
16099  } else { // Least-square solution for non-square systems.
16100 #ifdef cimg_use_lapack
16101  char TRANS = 'N';
16102  int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
16103  Ttfloat WORK_QUERY;
16104  Ttfloat
16105  * const lapA = new Ttfloat[M*N],
16106  * const lapB = new Ttfloat[M*NRHS];
16107  cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
16108  LWORK = (int) WORK_QUERY;
16109  Ttfloat *const WORK = new Ttfloat[LWORK];
16110  cimg_forXY(A,k,l) lapA[k*M+l] = (Ttfloat)(A(k,l));
16111  cimg_forXY(*this,k,l) lapB[k*M+l] = (Ttfloat)((*this)(k,l));
16112  cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
16113  if (INFO != 0)
16114  cimg::warn(_cimg_instance
16115  "solve(): LAPACK library function sgels() returned error code %d.",
16116  cimg_instance,
16117  INFO);
16118  assign(NRHS, N);
16119  if (!INFO != 0)
16120  cimg_forXY(*this,k,l) (*this)(k,l) = (T) lapB[k*M+l];
16121  else
16122  assign(A.get_pseudoinvert()*(*this));
16123  delete[] lapA; delete[] lapB; delete[] WORK;
16124 #else
16125  assign(A.get_pseudoinvert()*(*this));
16126 #endif
16127  }
16128  return *this;
16129  }
16130 
16132  template<typename t>
16134  return CImg<_cimg_Ttfloat>(*this,false).solve(A);
16135  }
16136 
16137  template<typename t, typename ti>
16138  CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
16139  typedef _cimg_Ttfloat Ttfloat;
16140  const int N = size();
16141  int ii = -1;
16142  Ttfloat sum;
16143  for (int i = 0; i<N; ++i) {
16144  const int ip = (int)indx[i];
16145  Ttfloat sum = (*this)(ip);
16146  (*this)(ip) = (*this)(i);
16147  if (ii>=0) for (int j = ii; j<=i-1; ++j) sum-=A(j,i)*(*this)(j);
16148  else if (sum!=0) ii = i;
16149  (*this)(i) = (T)sum;
16150  }
16151  for (int i = N - 1; i>=0; --i) {
16152  sum = (*this)(i);
16153  for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
16154  (*this)(i) = (T)(sum/A(i,i));
16155  }
16156  return *this;
16157  }
16158 
16160 
16166  template<typename t>
16168  const unsigned int siz = (int)size();
16169  if (A._width!=3 || A._height!=siz)
16170  throw CImgArgumentException(_cimg_instance
16171  "solve_tridiagonal(): Instance and tridiagonal matrix "
16172  "(%u,%u,%u,%u,%p) have incompatible dimensions.",
16173  cimg_instance,
16174  A._width,A._height,A._depth,A._spectrum,A._data);
16175  typedef _cimg_Ttfloat Ttfloat;
16176  const Ttfloat epsilon = 1e-4;
16177  CImg<Ttfloat> B = A.get_column(1), V(*this,false);
16178  for (int i = 1; i<(int)siz; ++i) {
16179  const Ttfloat m = A(0,i)/(B[i-1]?B[i-1]:epsilon);
16180  B[i] -= m*A(2,i-1);
16181  V[i] -= m*V[i-1];
16182  }
16183  (*this)[siz-1] = (T)(V[siz-1]/(B[siz-1]?B[siz-1]:epsilon));
16184  for (int i = (int)siz - 2; i>=0; --i) (*this)[i] = (T)((V[i] - A(2,i)*(*this)[i+1])/(B[i]?B[i]:epsilon));
16185  return *this;
16186  }
16187 
16189  template<typename t>
16191  return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
16192  }
16193 
16195 
16199  template<typename t>
16200  const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
16201  if (is_empty()) { val.assign(); vec.assign(); }
16202  else {
16203  if (_width!=_height || _depth>1 || _spectrum>1)
16204  throw CImgInstanceException(_cimg_instance
16205  "eigen(): Instance is not a square matrix.",
16206  cimg_instance);
16207 
16208  if (val.size()<(unsigned long)_width) val.assign(1,_width);
16209  if (vec.size()<(unsigned long)_width*_width) vec.assign(_width,_width);
16210  switch (_width) {
16211  case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
16212  case 2 : {
16213  const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
16214  double f = e*e - 4*(a*d - b*c);
16215  if (f<0)
16216  cimg::warn(_cimg_instance
16217  "eigen(): Complex eigenvalues found.",
16218  cimg_instance);
16219 
16220  f = std::sqrt(f);
16221  const double l1 = 0.5*(e-f), l2 = 0.5*(e+f);
16222  const double theta1 = std::atan2(l2-a,b), theta2 = std::atan2(l1-a,b);
16223  val[0] = (t)l2;
16224  val[1] = (t)l1;
16225  vec(0,0) = (t)std::cos(theta1);
16226  vec(0,1) = (t)std::sin(theta1);
16227  vec(1,0) = (t)std::cos(theta2);
16228  vec(1,1) = (t)std::sin(theta2);
16229  } break;
16230  default :
16231  throw CImgInstanceException(_cimg_instance
16232  "eigen(): Eigenvalues computation of general matrices is limited to 2x2 matrices.",
16233  cimg_instance);
16234  }
16235  }
16236  return *this;
16237  }
16238 
16240 
16244  CImgList<Tfloat> res(2);
16245  eigen(res[0],res[1]);
16246  return res;
16247  }
16248 
16250 
16254  template<typename t>
16255  const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
16256  if (is_empty()) { val.assign(); vec.assign(); }
16257  else {
16258 #ifdef cimg_use_lapack
16259  char JOB = 'V', UPLO = 'U';
16260  int N = _width, LWORK = 4*N, INFO;
16261  Tfloat
16262  *const lapA = new Tfloat[N*N],
16263  *const lapW = new Tfloat[N],
16264  *const WORK = new Tfloat[LWORK];
16265  cimg_forXY(*this,k,l) lapA[k*N+l] = (Tfloat)((*this)(k,l));
16266  cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
16267  if (INFO)
16268  cimg::warn(_cimg_instance
16269  "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
16270  cimg_instance,
16271  INFO);
16272 
16273  val.assign(1,N);
16274  vec.assign(N,N);
16275  if (!INFO) {
16276  cimg_forY(val,i) val(i) = (T)lapW[N-1-i];
16277  cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N-1-k)*N+l]);
16278  } else { val.fill(0); vec.fill(0); }
16279  delete[] lapA; delete[] lapW; delete[] WORK;
16280 #else
16281  if (_width!=_height || _depth>1 || _spectrum>1)
16282  throw CImgInstanceException(_cimg_instance
16283  "eigen(): Instance is not a square matrix.",
16284  cimg_instance);
16285 
16286  val.assign(1,_width);
16287  if (vec._data) vec.assign(_width,_width);
16288  if (_width<3) {
16289  eigen(val,vec);
16290  if (_width==2) { vec[1] = -vec[2]; vec[3] = vec[0]; } // Force orthogonality for 2x2 matrices.
16291  return *this;
16292  }
16293  CImg<t> V(_width,_width);
16294  SVD(vec,val,V,false);
16295  bool is_ambiguous = false;
16296  float eig = 0;
16297  cimg_forY(val,p) { // check for ambiguous cases.
16298  if (val[p]>eig) eig = (float)val[p];
16299  t scal = 0;
16300  cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
16301  if (cimg::abs(scal)<0.9f) is_ambiguous = true;
16302  if (scal<0) val[p] = -val[p];
16303  }
16304  if (is_ambiguous) {
16305  ++(eig*=2);
16306  SVD(vec,val,V,false,40,eig);
16307  val-=eig;
16308  }
16309  CImg<intT> permutations; // sort eigenvalues in decreasing order
16310  CImg<t> tmp(_width);
16311  val.sort(permutations,false);
16312  cimg_forY(vec,k) {
16313  cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
16314  std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
16315  }
16316 #endif
16317  }
16318  return *this;
16319  }
16320 
16322 
16326  CImgList<Tfloat> res(2);
16327  symmetric_eigen(res[0],res[1]);
16328  return res;
16329  }
16330 
16332 
16336  template<typename t>
16337  CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
16338  permutations.assign(_width,_height,_depth,_spectrum);
16339  if (is_empty()) return *this;
16340  cimg_foroff(permutations,off) permutations[off] = (t)off;
16341  return _quicksort(0,size()-1,permutations,is_increasing,true);
16342  }
16343 
16345  template<typename t>
16346  CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
16347  return (+*this).sort(permutations,is_increasing);
16348  }
16349 
16351 
16360  CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
16361  if (is_empty()) return *this;
16362  CImg<uintT> perm;
16363  switch (cimg::uncase(axis)) {
16364  case 0 :
16365  _quicksort(0,size()-1,perm,is_increasing,false);
16366  break;
16367  case 'x' : {
16368  perm.assign(_width);
16369  get_crop(0,0,0,0,_width-1,0,0,0).sort(perm,is_increasing);
16370  CImg<T> img(*this,false);
16371  cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
16372  } break;
16373  case 'y' : {
16374  perm.assign(_height);
16375  get_crop(0,0,0,0,0,_height-1,0,0).sort(perm,is_increasing);
16376  CImg<T> img(*this,false);
16377  cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
16378  } break;
16379  case 'z' : {
16380  perm.assign(_depth);
16381  get_crop(0,0,0,0,0,0,_depth-1,0).sort(perm,is_increasing);
16382  CImg<T> img(*this,false);
16383  cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
16384  } break;
16385  case 'c' : {
16386  perm.assign(_spectrum);
16387  get_crop(0,0,0,0,0,0,0,_spectrum-1).sort(perm,is_increasing);
16388  CImg<T> img(*this,false);
16389  cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
16390  } break;
16391  default :
16392  throw CImgArgumentException(_cimg_instance
16393  "sort(): Invalid specified axis '%c' "
16394  "(should be { x | y | z | c }).",
16395  cimg_instance,axis);
16396  }
16397  return *this;
16398  }
16399 
16401  CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
16402  return (+*this).sort(is_increasing,axis);
16403  }
16404 
16405  template<typename t>
16406  CImg<T>& _quicksort(const int indm, const int indM, CImg<t>& permutations, const bool is_increasing, const bool is_permutations) {
16407  if (indm<indM) {
16408  const int mid = (indm + indM)/2;
16409  if (is_increasing) {
16410  if ((*this)[indm]>(*this)[mid]) {
16411  cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
16412  }
16413  if ((*this)[mid]>(*this)[indM]) {
16414  cimg::swap((*this)[indM],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
16415  }
16416  if ((*this)[indm]>(*this)[mid]) {
16417  cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
16418  }
16419  } else {
16420  if ((*this)[indm]<(*this)[mid]) {
16421  cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
16422  }
16423  if ((*this)[mid]<(*this)[indM]) {
16424  cimg::swap((*this)[indM],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
16425  }
16426  if ((*this)[indm]<(*this)[mid]) {
16427  cimg::swap((*this)[indm],(*this)[mid]); if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
16428  }
16429  }
16430  if (indM - indm>=3) {
16431  const T pivot = (*this)[mid];
16432  int i = indm, j = indM;
16433  if (is_increasing) {
16434  do {
16435  while ((*this)[i]<pivot) ++i;
16436  while ((*this)[j]>pivot) --j;
16437  if (i<=j) {
16438  if (is_permutations) cimg::swap(permutations[i],permutations[j]);
16439  cimg::swap((*this)[i++],(*this)[j--]);
16440  }
16441  } while (i<=j);
16442  } else {
16443  do {
16444  while ((*this)[i]>pivot) ++i;
16445  while ((*this)[j]<pivot) --j;
16446  if (i<=j) {
16447  if (is_permutations) cimg::swap(permutations[i],permutations[j]);
16448  cimg::swap((*this)[i++],(*this)[j--]);
16449  }
16450  } while (i<=j);
16451  }
16452  if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
16453  if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
16454  }
16455  }
16456  return *this;
16457  }
16458 
16460 
16476  template<typename t>
16477  const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
16478  const unsigned int max_iteration=40, const float lambda=0) const {
16479  if (is_empty()) { U.assign(); S.assign(); V.assign(); }
16480  else {
16481  U = *this;
16482  if (lambda!=0) {
16483  const unsigned int delta = cimg::min(U._width,U._height);
16484  for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
16485  }
16486  if (S.size()<_width) S.assign(1,_width);
16487  if (V._width<_width || V._height<_height) V.assign(_width,_width);
16488  CImg<t> rv1(_width);
16489  t anorm = 0, c, f, g = 0, h, s, scale = 0;
16490  int l = 0, nm = 0;
16491 
16492  cimg_forX(U,i) {
16493  l = i+1; rv1[i] = scale*g; g = s = scale = 0;
16494  if (i<height()) {
16495  for (int k = i; k<height(); ++k) scale+= cimg::abs(U(i,k));
16496  if (scale) {
16497  for (int k = i; k<height(); ++k) { U(i,k)/=scale; s+= U(i,k)*U(i,k); }
16498  f = U(i,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h=f*g-s; U(i,i) = f-g;
16499  for (int j = l; j<width(); ++j) {
16500  s = 0;
16501  for (int k=i; k<height(); ++k) s+= U(i,k)*U(j,k);
16502  f = s/h;
16503  for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
16504  }
16505  for (int k = i; k<height(); ++k) U(i,k)*= scale;
16506  }
16507  }
16508  S[i]=scale*g;
16509 
16510  g = s = scale = 0;
16511  if (i<height() && i!=width()-1) {
16512  for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
16513  if (scale) {
16514  for (int k = l; k<width(); ++k) { U(k,i)/= scale; s+= U(k,i)*U(k,i); }
16515  f = U(l,i); g = (t)((f>=0?-1:1)*std::sqrt(s)); h = f*g-s; U(l,i) = f-g;
16516  for (int k = l; k<width(); ++k) rv1[k]=U(k,i)/h;
16517  for (int j = l; j<height(); ++j) {
16518  s = 0;
16519  for (int k = l; k<width(); ++k) s+= U(k,j)*U(k,i);
16520  for (int k = l; k<width(); ++k) U(k,j)+= s*rv1[k];
16521  }
16522  for (int k = l; k<width(); ++k) U(k,i)*= scale;
16523  }
16524  }
16525  anorm = (t)cimg::max((float)anorm,(float)(cimg::abs(S[i])+cimg::abs(rv1[i])));
16526  }
16527 
16528  for (int i = width()-1; i>=0; --i) {
16529  if (i<width()-1) {
16530  if (g) {
16531  for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
16532  for (int j = l; j<width(); ++j) {
16533  s = 0;
16534  for (int k = l; k<width(); ++k) s+= U(k,i)*V(j,k);
16535  for (int k = l; k<width(); ++k) V(j,k)+= s*V(i,k);
16536  }
16537  }
16538  for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.0;
16539  }
16540  V(i,i) = (t)1.0; g = rv1[i]; l = i;
16541  }
16542 
16543  for (int i = cimg::min(width(),height())-1; i>=0; --i) {
16544  l = i+1; g = S[i];
16545  for (int j = l; j<width(); ++j) U(j,i) = 0;
16546  if (g) {
16547  g = 1/g;
16548  for (int j = l; j<width(); ++j) {
16549  s = 0; for (int k = l; k<height(); ++k) s+= U(i,k)*U(j,k);
16550  f = (s/U(i,i))*g;
16551  for (int k = i; k<height(); ++k) U(j,k)+= f*U(i,k);
16552  }
16553  for (int j = i; j<height(); ++j) U(i,j)*= g;
16554  } else for (int j = i; j<height(); ++j) U(i,j) = 0;
16555  ++U(i,i);
16556  }
16557 
16558  for (int k = width()-1; k>=0; --k) {
16559  for (unsigned int its = 0; its<max_iteration; ++its) {
16560  bool flag = true;
16561  for (l = k; l>=1; --l) {
16562  nm = l-1;
16563  if ((cimg::abs(rv1[l])+anorm)==anorm) { flag = false; break; }
16564  if ((cimg::abs(S[nm])+anorm)==anorm) break;
16565  }
16566  if (flag) {
16567  c = 0; s = 1;
16568  for (int i = l; i<=k; ++i) {
16569  f = s*rv1[i]; rv1[i] = c*rv1[i];
16570  if ((cimg::abs(f)+anorm)==anorm) break;
16571  g = S[i]; h = (t)cimg::_pythagore(f,g); S[i] = h; h = 1/h; c = g*h; s = -f*h;
16572  cimg_forY(U,j) { const t y = U(nm,j), z = U(i,j); U(nm,j) = y*c + z*s; U(i,j) = z*c - y*s; }
16573  }
16574  }
16575  const t z = S[k];
16576  if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
16577  nm = k-1;
16578  t x = S[l], y = S[nm];
16579  g = rv1[nm]; h = rv1[k];
16580  f = ((y-z)*(y+z)+(g-h)*(g+h))/(2*h*y);
16581  g = (t)cimg::_pythagore(f,1.0);
16582  f = ((x-z)*(x+z)+h*((y/(f+ (f>=0?g:-g)))-h))/x;
16583  c = s = 1;
16584  for (int j = l; j<=nm; ++j) {
16585  const int i = j+1;
16586  g = rv1[i]; h = s*g; g = c*g;
16587  t y = S[i];
16588  t z = (t)cimg::_pythagore(f,h);
16589  rv1[j] = z; c = f/z; s = h/z;
16590  f = x*c+g*s; g = g*c-x*s; h = y*s; y*=c;
16591  cimg_forX(U,jj) { const t x = V(j,jj), z = V(i,jj); V(j,jj) = x*c + z*s; V(i,jj) = z*c - x*s; }
16592  z = (t)cimg::_pythagore(f,h); S[j] = z;
16593  if (z) { z = 1/z; c = f*z; s = h*z; }
16594  f = c*g+s*y; x = c*y-s*g;
16595  cimg_forY(U,jj) { const t y = U(j,jj); z = U(i,jj); U(j,jj) = y*c + z*s; U(i,jj) = z*c - y*s; }
16596  }
16597  rv1[l] = 0; rv1[k]=f; S[k]=x;
16598  }
16599  }
16600 
16601  if (sorting) {
16602  CImg<intT> permutations;
16603  CImg<t> tmp(_width);
16604  S.sort(permutations,false);
16605  cimg_forY(U,k) {
16606  cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
16607  std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
16608  }
16609  cimg_forY(V,k) {
16610  cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
16611  std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
16612  }
16613  }
16614  }
16615  return *this;
16616  }
16617 
16619 
16622  CImgList<Tfloat> get_SVD(const bool sorting=true,
16623  const unsigned int max_iteration=40, const float lambda=0) const {
16624  CImgList<Tfloat> res(3);
16625  SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
16626  return res;
16627  }
16628 
16629  // [internal] Compute the LU decomposition of a permuted matrix.
16630  template<typename t>
16631  CImg<T>& _LU(CImg<t>& indx, bool& d) {
16632  const int N = width();
16633  int imax = 0;
16634  CImg<Tfloat> vv(N);
16635  indx.assign(N);
16636  d = true;
16637  cimg_forX(*this,i) {
16638  Tfloat vmax = 0;
16639  cimg_forX(*this,j) {
16640  const Tfloat tmp = cimg::abs((*this)(j,i));
16641  if (tmp>vmax) vmax = tmp;
16642  }
16643  if (vmax==0) { indx.fill(0); return fill(0); }
16644  vv[i] = 1/vmax;
16645  }
16646  cimg_forX(*this,j) {
16647  for (int i = 0; i<j; ++i) {
16648  Tfloat sum=(*this)(j,i);
16649  for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
16650  (*this)(j,i) = (T)sum;
16651  }
16652  Tfloat vmax = 0;
16653  for (int i = j; i<width(); ++i) {
16654  Tfloat sum=(*this)(j,i);
16655  for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
16656  (*this)(j,i) = (T)sum;
16657  const Tfloat tmp = vv[i]*cimg::abs(sum);
16658  if (tmp>=vmax) { vmax=tmp; imax=i; }
16659  }
16660  if (j!=imax) {
16661  cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
16662  d =!d;
16663  vv[imax] = vv[j];
16664  }
16665  indx[j] = (t)imax;
16666  if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
16667  if (j<N) {
16668  const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
16669  for (int i=j+1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
16670  }
16671  }
16672  return *this;
16673  }
16674 
16676 
16684  template<typename tf, typename t>
16685  static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
16686  const unsigned int starting_node, const unsigned int ending_node,
16687  CImg<t>& previous_node) {
16688  if (starting_node>=nb_nodes)
16689  throw CImgArgumentException("CImg<%s>::dijkstra(): Specified indice of starting node %u is higher than number of nodes %u.",
16690  pixel_type(),starting_node,nb_nodes);
16691  CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
16692  dist(starting_node) = 0;
16693  previous_node.assign(1,nb_nodes,1,1,(t)-1);
16694  previous_node(starting_node) = (t)starting_node;
16695  CImg<uintT> Q(nb_nodes);
16696  cimg_forX(Q,u) Q(u) = u;
16697  cimg::swap(Q(starting_node),Q(0));
16698  unsigned int sizeQ = nb_nodes;
16699  while (sizeQ) {
16700  // Update neighbors from minimal vertex
16701  const unsigned int umin = Q(0);
16702  if (umin==ending_node) sizeQ = 0;
16703  else {
16704  const T dmin = dist(umin);
16705  const T infty = cimg::type<T>::max();
16706  for (unsigned int q = 1; q<sizeQ; ++q) {
16707  const unsigned int v = Q(q);
16708  const T d = (T)distance(v,umin);
16709  if (d<infty) {
16710  const T alt = dmin + d;
16711  if (alt<dist(v)) {
16712  dist(v) = alt;
16713  previous_node(v) = (t)umin;
16714  const T distpos = dist(Q(q));
16715  for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos+1)/2-1)); pos=par) cimg::swap(Q(pos),Q(par));
16716  }
16717  }
16718  }
16719  // Remove minimal vertex from queue
16720  Q(0) = Q(--sizeQ);
16721  const T distpos = dist(Q(0));
16722  for (unsigned int pos = 0, left = 0, right = 0;
16723  ((right=2*(pos+1),(left=right-1))<sizeQ && distpos>dist(Q(left))) || (right<sizeQ && distpos>dist(Q(right)));) {
16724  if (right<sizeQ) {
16725  if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
16726  else { cimg::swap(Q(pos),Q(right)); pos = right; }
16727  } else { cimg::swap(Q(pos),Q(left)); pos = left; }
16728  }
16729  }
16730  }
16731  return dist;
16732  }
16733 
16735  template<typename tf, typename t>
16736  static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
16737  const unsigned int starting_node, const unsigned int ending_node=~0U) {
16738  CImg<uintT> foo;
16739  return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
16740  }
16741 
16743 
16750  template<typename t>
16751  CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& previous_node) {
16752  return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
16753  }
16754 
16756  template<typename t>
16757  CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node, CImg<t>& previous_node) const {
16758  if (_width!=_height || _depth!=1 || _spectrum!=1)
16759  throw CImgInstanceException(_cimg_instance
16760  "dijkstra(): Instance is not a graph adjacency matrix.",
16761  cimg_instance);
16762 
16763  return dijkstra(*this,_width,starting_node,ending_node,previous_node);
16764  }
16765 
16767  CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
16768  return get_dijkstra(starting_node,ending_node).move_to(*this);
16769  }
16770 
16772  CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
16773  CImg<uintT> foo;
16774  return get_dijkstra(starting_node,ending_node,foo);
16775  }
16776 
16778 
16782  static CImg<T> string(const char *const str, const bool is_last_zero=true) {
16783  if (!str) return CImg<T>();
16784  return CImg<T>(str,std::strlen(str)+(is_last_zero?1:0));
16785  }
16786 
16788 
16791  static CImg<T> vector(const T& a0) {
16792  _cimg_static CImg<T> r(1,1);
16793  r[0] = a0;
16794  return r;
16795  }
16796 
16798 
16802  static CImg<T> vector(const T& a0, const T& a1) {
16803  _cimg_static CImg<T> r(1,2); T *ptr = r._data;
16804  *(ptr++) = a0; *(ptr++) = a1;
16805  return r;
16806  }
16807 
16809 
16814  static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
16815  _cimg_static CImg<T> r(1,3); T *ptr = r._data;
16816  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
16817  return r;
16818  }
16819 
16821 
16827  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
16828  _cimg_static CImg<T> r(1,4); T *ptr = r._data;
16829  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16830  return r;
16831  }
16832 
16834  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
16835  _cimg_static CImg<T> r(1,5); T *ptr = r._data;
16836  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
16837  return r;
16838  }
16839 
16841  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
16842  _cimg_static CImg<T> r(1,6); T *ptr = r._data;
16843  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
16844  return r;
16845  }
16846 
16848  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16849  const T& a4, const T& a5, const T& a6) {
16850  _cimg_static CImg<T> r(1,7); T *ptr = r._data;
16851  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16852  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6;
16853  return r;
16854  }
16855 
16857  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16858  const T& a4, const T& a5, const T& a6, const T& a7) {
16859  _cimg_static CImg<T> r(1,8); T *ptr = r._data;
16860  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16861  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16862  return r;
16863  }
16864 
16866  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16867  const T& a4, const T& a5, const T& a6, const T& a7,
16868  const T& a8) {
16869  _cimg_static CImg<T> r(1,9); T *ptr = r._data;
16870  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16871  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16872  *(ptr++) = a8;
16873  return r;
16874  }
16875 
16877  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16878  const T& a4, const T& a5, const T& a6, const T& a7,
16879  const T& a8, const T& a9) {
16880  _cimg_static CImg<T> r(1,10); T *ptr = r._data;
16881  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16882  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16883  *(ptr++) = a8; *(ptr++) = a9;
16884  return r;
16885  }
16886 
16888  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16889  const T& a4, const T& a5, const T& a6, const T& a7,
16890  const T& a8, const T& a9, const T& a10) {
16891  _cimg_static CImg<T> r(1,11); T *ptr = r._data;
16892  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16893  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16894  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10;
16895  return r;
16896  }
16897 
16899  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16900  const T& a4, const T& a5, const T& a6, const T& a7,
16901  const T& a8, const T& a9, const T& a10, const T& a11) {
16902  _cimg_static CImg<T> r(1,12); T *ptr = r._data;
16903  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16904  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16905  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16906  return r;
16907  }
16908 
16910  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16911  const T& a4, const T& a5, const T& a6, const T& a7,
16912  const T& a8, const T& a9, const T& a10, const T& a11,
16913  const T& a12) {
16914  _cimg_static CImg<T> r(1,13); T *ptr = r._data;
16915  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16916  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16917  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16918  *(ptr++) = a12;
16919  return r;
16920  }
16921 
16923  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16924  const T& a4, const T& a5, const T& a6, const T& a7,
16925  const T& a8, const T& a9, const T& a10, const T& a11,
16926  const T& a12, const T& a13) {
16927  _cimg_static CImg<T> r(1,14); T *ptr = r._data;
16928  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16929  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16930  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16931  *(ptr++) = a12; *(ptr++) = a13;
16932  return r;
16933  }
16934 
16936  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16937  const T& a4, const T& a5, const T& a6, const T& a7,
16938  const T& a8, const T& a9, const T& a10, const T& a11,
16939  const T& a12, const T& a13, const T& a14) {
16940  _cimg_static CImg<T> r(1,15); T *ptr = r._data;
16941  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16942  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16943  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16944  *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
16945  return r;
16946  }
16947 
16949  static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
16950  const T& a4, const T& a5, const T& a6, const T& a7,
16951  const T& a8, const T& a9, const T& a10, const T& a11,
16952  const T& a12, const T& a13, const T& a14, const T& a15) {
16953  _cimg_static CImg<T> r(1,16); T *ptr = r._data;
16954  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
16955  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
16956  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
16957  *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
16958  return r;
16959  }
16960 
16962 
16966  static CImg<T> matrix(const T& a0) {
16967  return vector(a0);
16968  }
16969 
16971 
16977  static CImg<T> matrix(const T& a0, const T& a1,
16978  const T& a2, const T& a3) {
16979  _cimg_static CImg<T> r(2,2); T *ptr = r._data;
16980  *(ptr++) = a0; *(ptr++) = a1;
16981  *(ptr++) = a2; *(ptr++) = a3;
16982  return r;
16983  }
16984 
16986 
16997  static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
16998  const T& a3, const T& a4, const T& a5,
16999  const T& a6, const T& a7, const T& a8) {
17000  _cimg_static CImg<T> r(3,3); T *ptr = r._data;
17001  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
17002  *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
17003  *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
17004  return r;
17005  }
17006 
17008  static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
17009  const T& a4, const T& a5, const T& a6, const T& a7,
17010  const T& a8, const T& a9, const T& a10, const T& a11,
17011  const T& a12, const T& a13, const T& a14, const T& a15) {
17012  _cimg_static CImg<T> r(4,4); T *ptr = r._data;
17013  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
17014  *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
17015  *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
17016  *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
17017  return r;
17018  }
17019 
17021  static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
17022  const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
17023  const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
17024  const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
17025  const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
17026  _cimg_static CImg<T> r(5,5); T *ptr = r._data;
17027  *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
17028  *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
17029  *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
17030  *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
17031  *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
17032  return r;
17033  }
17034 
17036 
17040  static CImg<T> tensor(const T& a0) {
17041  return matrix(a0);
17042  }
17043 
17045  static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
17046  return matrix(a0,a1,a1,a2);
17047  }
17048 
17050  static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
17051  return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
17052  }
17053 
17055  static CImg<T> diagonal(const T& a0) {
17056  return matrix(a0);
17057  }
17058 
17060  static CImg<T> diagonal(const T& a0, const T& a1) {
17061  return matrix(a0,0,0,a1);
17062  }
17063 
17065  static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
17066  return matrix(a0,0,0,0,a1,0,0,0,a2);
17067  }
17068 
17070  static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
17071  return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
17072  }
17073 
17075  static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
17076  return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4);
17077  }
17078 
17080 
17083  static CImg<T> identity_matrix(const unsigned int N) {
17084  CImg<T> res(N,N,1,1,0);
17085  cimg_forX(res,x) res(x,x) = 1;
17086  return res;
17087  }
17088 
17090 
17095  static CImg<T> sequence(const unsigned int N, const T a0, const T a1) {
17096  if (N) return CImg<T>(1,N).sequence(a0,a1);
17097  return CImg<T>();
17098  }
17099 
17101 
17108  static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w, const bool is_quaternion=false) {
17109  float X,Y,Z,W;
17110  if (!is_quaternion) {
17111  const float norm = (float)std::sqrt(x*x + y*y + z*z),
17112  nx = norm>0?x/norm:0,
17113  ny = norm>0?y/norm:0,
17114  nz = norm>0?z/norm:1,
17115  nw = norm>0?w:0,
17116  sina = (float)std::sin(nw/2),
17117  cosa = (float)std::cos(nw/2);
17118  X = nx*sina;
17119  Y = ny*sina;
17120  Z = nz*sina;
17121  W = cosa;
17122  } else {
17123  const float norm = (float)std::sqrt(x*x + y*y + z*z + w*w);
17124  if (norm>0) { X = x/norm; Y = y/norm; Z = z/norm; W = w/norm; }
17125  else { X = Y = Z = 0; W = 1; }
17126  }
17127  const float xx = X*X, xy = X*Y, xz = X*Z, xw = X*W, yy = Y*Y, yz = Y*Z, yw = Y*W, zz = Z*Z, zw = Z*W;
17128  return CImg<T>::matrix((T)(1-2*(yy+zz)), (T)(2*(xy+zw)), (T)(2*(xz-yw)),
17129  (T)(2*(xy-zw)), (T)(1-2*(xx+zz)), (T)(2*(yz+xw)),
17130  (T)(2*(xz+yw)), (T)(2*(yz-xw)), (T)(1-2*(xx+yy)));
17131  }
17132 
17134  //-----------------------------------
17135  //
17137 
17138  //-----------------------------------
17139 
17141 
17144  CImg<T>& fill(const T val) {
17145  if (is_empty()) return *this;
17146  if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
17147  else std::memset(_data,(int)val,sizeof(T)*size());
17148  return *this;
17149  }
17150 
17152  CImg<T> get_fill(const T val) const {
17153  return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
17154  }
17155 
17157 
17161  CImg<T>& fill(const T val0, const T val1) {
17162  if (is_empty()) return *this;
17163  T *ptrd, *ptre = end()-1;
17164  for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
17165  if (ptrd!=ptre+1) *(ptrd++) = val0;
17166  return *this;
17167  }
17168 
17170  CImg<T> get_fill(const T val0, const T val1) const {
17171  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
17172  }
17173 
17175  CImg<T>& fill(const T val0, const T val1, const T val2) {
17176  if (is_empty()) return *this;
17177  T *ptrd, *ptre = end()-2;
17178  for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
17179  ptre+=2;
17180  switch (ptre - ptrd) {
17181  case 2 : *(--ptre) = val1;
17182  case 1 : *(--ptre) = val0;
17183  }
17184  return *this;
17185  }
17186 
17188  CImg<T> get_fill(const T val0, const T val1, const T val2) const {
17189  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
17190  }
17191 
17193  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3) {
17194  if (is_empty()) return *this;
17195  T *ptrd, *ptre = end()-3;
17196  for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
17197  ptre+=3;
17198  switch (ptre - ptrd) {
17199  case 3 : *(--ptre) = val2;
17200  case 2 : *(--ptre) = val1;
17201  case 1 : *(--ptre) = val0;
17202  }
17203  return *this;
17204  }
17205 
17207  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3) const {
17208  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
17209  }
17210 
17212  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4) {
17213  if (is_empty()) return *this;
17214  T *ptrd, *ptre = end()-4;
17215  for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; }
17216  ptre+=4;
17217  switch (ptre - ptrd) {
17218  case 4 : *(--ptre) = val3;
17219  case 3 : *(--ptre) = val2;
17220  case 2 : *(--ptre) = val1;
17221  case 1 : *(--ptre) = val0;
17222  }
17223  return *this;
17224  }
17225 
17227  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4) const {
17228  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
17229  }
17230 
17232  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) {
17233  if (is_empty()) return *this;
17234  T *ptrd, *ptre = end()-5;
17235  for (ptrd = _data; ptrd<ptre; ) {
17236  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17237  }
17238  ptre+=5;
17239  switch (ptre - ptrd) {
17240  case 5 : *(--ptre) = val4;
17241  case 4 : *(--ptre) = val3;
17242  case 3 : *(--ptre) = val2;
17243  case 2 : *(--ptre) = val1;
17244  case 1 : *(--ptre) = val0;
17245  }
17246  return *this;
17247  }
17248 
17250  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5) const {
17251  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
17252  }
17253 
17255  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) {
17256  if (is_empty()) return *this;
17257  T *ptrd, *ptre = end()-6;
17258  for (ptrd = _data; ptrd<ptre; ) {
17259  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6;
17260  }
17261  ptre+=6;
17262  switch (ptre - ptrd) {
17263  case 6 : *(--ptre) = val5;
17264  case 5 : *(--ptre) = val4;
17265  case 4 : *(--ptre) = val3;
17266  case 3 : *(--ptre) = val2;
17267  case 2 : *(--ptre) = val1;
17268  case 1 : *(--ptre) = val0;
17269  }
17270  return *this;
17271  }
17272 
17274  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6) const {
17275  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
17276  }
17277 
17279  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17280  const T val7) {
17281  if (is_empty()) return *this;
17282  T *ptrd, *ptre = end()-7;
17283  for (ptrd = _data; ptrd<ptre; ) {
17284  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
17285  *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
17286  }
17287  ptre+=7;
17288  switch (ptre - ptrd) {
17289  case 7 : *(--ptre) = val6;
17290  case 6 : *(--ptre) = val5;
17291  case 5 : *(--ptre) = val4;
17292  case 4 : *(--ptre) = val3;
17293  case 3 : *(--ptre) = val2;
17294  case 2 : *(--ptre) = val1;
17295  case 1 : *(--ptre) = val0;
17296  }
17297  return *this;
17298  }
17299 
17301  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17302  const T val7) const {
17303  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
17304  }
17305 
17307  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17308  const T val7, const T val8) {
17309  if (is_empty()) return *this;
17310  T *ptrd, *ptre = end()-8;
17311  for (ptrd = _data; ptrd<ptre; ) {
17312  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
17313  *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17314  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
17315  }
17316  ptre+=8;
17317  switch (ptre - ptrd) {
17318  case 8 : *(--ptre) = val7;
17319  case 7 : *(--ptre) = val6;
17320  case 6 : *(--ptre) = val5;
17321  case 5 : *(--ptre) = val4;
17322  case 4 : *(--ptre) = val3;
17323  case 3 : *(--ptre) = val2;
17324  case 2 : *(--ptre) = val1;
17325  case 1 : *(--ptre) = val0;
17326  }
17327  return *this;
17328  }
17329 
17331  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17332  const T val7, const T val8) const {
17333  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
17334  }
17335 
17337  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17338  const T val7, const T val8, const T val9) {
17339  if (is_empty()) return *this;
17340  T *ptrd, *ptre = end()-9;
17341  for (ptrd = _data; ptrd<ptre; ) {
17342  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
17343  *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
17344  }
17345  ptre+=9;
17346  switch (ptre - ptrd) {
17347  case 9 : *(--ptre) = val8;
17348  case 8 : *(--ptre) = val7;
17349  case 7 : *(--ptre) = val6;
17350  case 6 : *(--ptre) = val5;
17351  case 5 : *(--ptre) = val4;
17352  case 4 : *(--ptre) = val3;
17353  case 3 : *(--ptre) = val2;
17354  case 2 : *(--ptre) = val1;
17355  case 1 : *(--ptre) = val0;
17356  }
17357  return *this;
17358  }
17359 
17361  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17362  const T val7, const T val8, const T val9) const {
17363  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
17364  }
17365 
17367  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17368  const T val7, const T val8, const T val9, const T val10) {
17369  if (is_empty()) return *this;
17370  T *ptrd, *ptre = end()-10;
17371  for (ptrd = _data; ptrd<ptre; ) {
17372  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
17373  *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
17374  *(ptrd++) = val10;
17375  }
17376  ptre+=10;
17377  switch (ptre - ptrd) {
17378  case 10 : *(--ptre) = val9;
17379  case 9 : *(--ptre) = val8;
17380  case 8 : *(--ptre) = val7;
17381  case 7 : *(--ptre) = val6;
17382  case 6 : *(--ptre) = val5;
17383  case 5 : *(--ptre) = val4;
17384  case 4 : *(--ptre) = val3;
17385  case 3 : *(--ptre) = val2;
17386  case 2 : *(--ptre) = val1;
17387  case 1 : *(--ptre) = val0;
17388  }
17389  return *this;
17390  }
17391 
17393  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17394  const T val7, const T val8, const T val9, const T val10) const {
17395  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
17396  }
17397 
17399  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17400  const T val7, const T val8, const T val9, const T val10, const T val11) {
17401  if (is_empty()) return *this;
17402  T *ptrd, *ptre = end()-11;
17403  for (ptrd = _data; ptrd<ptre; ) {
17404  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17405  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17406  }
17407  ptre+=11;
17408  switch (ptre - ptrd) {
17409  case 11 : *(--ptre) = val10;
17410  case 10 : *(--ptre) = val9;
17411  case 9 : *(--ptre) = val8;
17412  case 8 : *(--ptre) = val7;
17413  case 7 : *(--ptre) = val6;
17414  case 6 : *(--ptre) = val5;
17415  case 5 : *(--ptre) = val4;
17416  case 4 : *(--ptre) = val3;
17417  case 3 : *(--ptre) = val2;
17418  case 2 : *(--ptre) = val1;
17419  case 1 : *(--ptre) = val0;
17420  }
17421  return *this;
17422  }
17423 
17425  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17426  const T val7, const T val8, const T val9, const T val10, const T val11) const {
17427  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11);
17428  }
17429 
17431  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17432  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) {
17433  if (is_empty()) return *this;
17434  T *ptrd, *ptre = end()-12;
17435  for (ptrd = _data; ptrd<ptre; ) {
17436  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17437  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17438  *(ptrd++) = val12;
17439  }
17440  ptre+=12;
17441  switch (ptre - ptrd) {
17442  case 12 : *(--ptre) = val11;
17443  case 11 : *(--ptre) = val10;
17444  case 10 : *(--ptre) = val9;
17445  case 9 : *(--ptre) = val8;
17446  case 8 : *(--ptre) = val7;
17447  case 7 : *(--ptre) = val6;
17448  case 6 : *(--ptre) = val5;
17449  case 5 : *(--ptre) = val4;
17450  case 4 : *(--ptre) = val3;
17451  case 3 : *(--ptre) = val2;
17452  case 2 : *(--ptre) = val1;
17453  case 1 : *(--ptre) = val0;
17454  }
17455  return *this;
17456  }
17457 
17459  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17460  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12) const {
17461  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12);
17462  }
17463 
17465  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17466  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17467  const T val13) {
17468  if (is_empty()) return *this;
17469  T *ptrd, *ptre = end()-13;
17470  for (ptrd = _data; ptrd<ptre; ) {
17471  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17472  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17473  *(ptrd++) = val12; *(ptrd++) = val13;
17474  }
17475  ptre+=13;
17476  switch (ptre - ptrd) {
17477  case 13 : *(--ptre) = val12;
17478  case 12 : *(--ptre) = val11;
17479  case 11 : *(--ptre) = val10;
17480  case 10 : *(--ptre) = val9;
17481  case 9 : *(--ptre) = val8;
17482  case 8 : *(--ptre) = val7;
17483  case 7 : *(--ptre) = val6;
17484  case 6 : *(--ptre) = val5;
17485  case 5 : *(--ptre) = val4;
17486  case 4 : *(--ptre) = val3;
17487  case 3 : *(--ptre) = val2;
17488  case 2 : *(--ptre) = val1;
17489  case 1 : *(--ptre) = val0;
17490  }
17491  return *this;
17492  }
17493 
17495  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17496  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17497  const T val13) const {
17498  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
17499  val13);
17500  }
17501 
17503  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17504  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17505  const T val13, const T val14) {
17506  if (is_empty()) return *this;
17507  T *ptrd, *ptre = end()-14;
17508  for (ptrd = _data; ptrd<ptre; ) {
17509  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17510  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17511  *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
17512  }
17513  ptre+=14;
17514  switch (ptre - ptrd) {
17515  case 14 : *(--ptre) = val13;
17516  case 13 : *(--ptre) = val12;
17517  case 12 : *(--ptre) = val11;
17518  case 11 : *(--ptre) = val10;
17519  case 10 : *(--ptre) = val9;
17520  case 9 : *(--ptre) = val8;
17521  case 8 : *(--ptre) = val7;
17522  case 7 : *(--ptre) = val6;
17523  case 6 : *(--ptre) = val5;
17524  case 5 : *(--ptre) = val4;
17525  case 4 : *(--ptre) = val3;
17526  case 3 : *(--ptre) = val2;
17527  case 2 : *(--ptre) = val1;
17528  case 1 : *(--ptre) = val0;
17529  }
17530  return *this;
17531  }
17532 
17534  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17535  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17536  const T val13, const T val14) const {
17537  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
17538  val13,val14);
17539  }
17540 
17542  CImg<T>& fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17543  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17544  const T val13, const T val14, const T val15) {
17545  if (is_empty()) return *this;
17546  T *ptrd, *ptre = end()-15;
17547  for (ptrd = _data; ptrd<ptre; ) {
17548  *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
17549  *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
17550  *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
17551  }
17552  ptre+=15;
17553  switch (ptre - ptrd) {
17554  case 15 : *(--ptre) = val14;
17555  case 14 : *(--ptre) = val13;
17556  case 13 : *(--ptre) = val12;
17557  case 12 : *(--ptre) = val11;
17558  case 11 : *(--ptre) = val10;
17559  case 10 : *(--ptre) = val9;
17560  case 9 : *(--ptre) = val8;
17561  case 8 : *(--ptre) = val7;
17562  case 7 : *(--ptre) = val6;
17563  case 6 : *(--ptre) = val5;
17564  case 5 : *(--ptre) = val4;
17565  case 4 : *(--ptre) = val3;
17566  case 3 : *(--ptre) = val2;
17567  case 2 : *(--ptre) = val1;
17568  case 1 : *(--ptre) = val0;
17569  }
17570  return *this;
17571  }
17572 
17574  CImg<T> get_fill(const T val0, const T val1, const T val2, const T val3, const T val4, const T val5, const T val6,
17575  const T val7, const T val8, const T val9, const T val10, const T val11, const T val12,
17576  const T val13, const T val14, const T val15) const {
17577  return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,val11,val12,
17578  val13,val14,val15);
17579  }
17580 
17582 
17586  CImg<T>& fill(const char *const expression, const bool repeat_flag) {
17587  if (is_empty() || !expression || !*expression) return *this;
17588  const unsigned int omode = cimg::exception_mode();
17589  cimg::exception_mode() = 0;
17590  try { // Try to fill values according to a formula.
17591  const CImg<T> _base = std::strstr(expression,"i(")?+*this:CImg<T>(), &base = _base?_base:*this;
17592  _cimg_math_parser mp(base,expression,"fill");
17593  T *ptrd = _data;
17594  cimg_forXYZC(*this,x,y,z,c) *(ptrd++) = (T)mp.eval((double)x,(double)y,(double)z,(double)c);
17595  } catch (CImgException& e) { // If failed, try to recognize a list of values.
17596  char item[16384] = { 0 }, sep = 0;
17597  const char *nexpression = expression;
17598  unsigned long nb = 0;
17599  const unsigned long siz = size();
17600  T *ptrd = _data;
17601  for (double val = 0; *nexpression && nb<siz; ++nb) {
17602  sep = 0;
17603  const int err = std::sscanf(nexpression,"%4095[ \n\t0-9.e+-]%c",item,&sep);
17604  if (err>0 && std::sscanf(item,"%lf",&val)==1) {
17605  nexpression+=std::strlen(item) + (err>1?1:0);
17606  *(ptrd++) = (T)val;
17607  } else break;
17608  }
17609  cimg::exception_mode() = omode;
17610  if (nb<siz && (sep || *nexpression))
17611  throw CImgArgumentException(e.what(),pixel_type(),expression);
17612  if (repeat_flag && nb && nb<siz) for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
17613  }
17614  cimg::exception_mode() = omode;
17615  return *this;
17616  }
17617 
17619  CImg<T> get_fill(const char *const values, const bool repeat_values) const {
17620  return (+*this).fill(values,repeat_values);
17621  }
17622 
17624 
17628  template<typename t>
17629  CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
17630  if (is_empty() || !values) return *this;
17631  T *ptrd = _data, *ptre = ptrd + size();
17632  for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs) *(ptrd++) = (T)*ptrs;
17633  if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
17634  return *this;
17635  }
17636 
17638  template<typename t>
17639  CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
17640  return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):(+*this).fill(values,repeat_values);
17641  }
17642 
17644 
17650  CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
17651 #define _cimg_fill1(x,y,z,c,off,siz,t) { \
17652  va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
17653  for (unsigned long k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
17654  va_end(ap); }
17655  if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
17656  return *this;
17657  }
17658 
17660  CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
17661  if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
17662  return *this;
17663  }
17664 
17666 
17672  CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
17673  if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
17674  return *this;
17675  }
17676 
17678  CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
17679  if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
17680  return *this;
17681  }
17682 
17684 
17690  CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
17691  const unsigned long wh = (unsigned long)_width*_height;
17692  if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
17693  return *this;
17694  }
17695 
17697  CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
17698  const unsigned long wh = (unsigned long)_width*_height;
17699  if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
17700  return *this;
17701  }
17702 
17704 
17710  CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
17711  const unsigned long whd = (unsigned long)_width*_height*_depth;
17712  if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
17713  return *this;
17714  }
17715 
17717  CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
17718  const unsigned long whd = (unsigned long)_width*_height*_depth;
17719  if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
17720  return *this;
17721  }
17722 
17724 
17729  CImg<T>& discard(const T value) {
17730  return get_discard(value).move_to(*this);
17731  }
17732 
17734  CImg<T> get_discard(const T value) const {
17735  CImg<T> res(1,size());
17736  T *pd = res._data;
17737  for (const T *ps = _data, *const pse = end(); ps<pse; ++ps)
17738  if (*ps!=value) *(pd++) = *ps;
17739  if (pd==res._data) return CImg<T>();
17740  return res.resize(1,pd-res._data,1,1,-1);
17741  }
17742 
17744 
17749  template<typename t>
17750  CImg<T>& discard(const CImg<t>& values) {
17751  return get_discard(values).move_to(*this);
17752  }
17753 
17755  template<typename t>
17756  CImg<T> get_discard(const CImg<t>& values) const {
17757  if (!values) return *this;
17758  if (values.size()==1) return get_discard(*values);
17759  CImg<T> res(1,size());
17760  T *pd = res._data;
17761  const t *const pve = values.end();
17762  for (const T *ps = _data, *const pse = end(); ps<pse; ) {
17763  const T *_ps = ps;
17764  const t *pv = values._data;
17765  while (_ps<pse && pv<pve) { if (*(_ps++)!=(T)*pv) break; ++pv; }
17766  if (pv!=pve) {
17767  const unsigned int l = _ps - ps;
17768  if (l==1) *(pd++) = *ps; else { std::memcpy(pd,ps,sizeof(T)*l); pd+=l; }
17769  }
17770  ps = _ps;
17771  }
17772  if (pd==res._data) return CImg<T>();
17773  return res.resize(1,pd-res._data,1,1,-1);
17774  }
17775 
17777 
17780  cimg::invert_endianness(_data,size());
17781  return *this;
17782  }
17783 
17786  return (+*this).invert_endianness();
17787  }
17788 
17790 
17795  CImg<T>& rand(const T val_min, const T val_max) {
17796  const float delta = (float)val_max - (float)val_min;
17797  cimg_for(*this,ptrd,T) *ptrd = (T)(val_min + cimg::rand()*delta);
17798  return *this;
17799  }
17800 
17802  CImg<T> get_rand(const T val_min, const T val_max) const {
17803  return (+*this).rand(val_min,val_max);
17804  }
17805 
17807 
17814  CImg<T>& round(const double y=1, const int rounding_type=0) {
17815  if (y>0) cimg_for(*this,ptrd,T) *ptrd = cimg::round(*ptrd,y,rounding_type);
17816  return *this;
17817  }
17818 
17820  CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
17821  return (+*this).round(y,rounding_type);
17822  }
17823 
17825 
17839  CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
17840  if (!is_empty()) {
17841  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
17842  Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
17843  if (nsigma==0 && noise_type!=3) return *this;
17844  if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
17845  if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.0);
17846  switch (noise_type) {
17847  case 0 : { // Gaussian noise
17848  cimg_for(*this,ptrd,T) {
17849  Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::grand());
17850  if (val>vmax) val = vmax;
17851  if (val<vmin) val = vmin;
17852  *ptrd = (T)val;
17853  }
17854  } break;
17855  case 1 : { // Uniform noise
17856  cimg_for(*this,ptrd,T) {
17857  Tfloat val = (Tfloat)(*ptrd + nsigma*cimg::crand());
17858  if (val>vmax) val = vmax;
17859  if (val<vmin) val = vmin;
17860  *ptrd = (T)val;
17861  }
17862  } break;
17863  case 2 : { // Salt & Pepper noise
17864  if (nsigma<0) nsigma = -nsigma;
17865  if (M==m) { m = 0; M = (Tfloat)(cimg::type<T>::is_float()?1:cimg::type<T>::max()); }
17866  cimg_for(*this,ptrd,T) if (cimg::rand()*100<nsigma) *ptrd = (T)(cimg::rand()<0.5?M:m);
17867  } break;
17868 
17869  case 3 : { // Poisson Noise
17870  cimg_for(*this,ptrd,T) *ptrd = (T)cimg::prand(*ptrd);
17871  } break;
17872 
17873  case 4 : { // Rice noise
17874  const Tfloat sqrt2 = (Tfloat)std::sqrt(2.0);
17875  cimg_for(*this,ptrd,T) {
17876  const Tfloat
17877  val0 = (Tfloat)*ptrd/sqrt2,
17878  re = (Tfloat)(val0 + nsigma*cimg::grand()),
17879  im = (Tfloat)(val0 + nsigma*cimg::grand());
17880  Tfloat val = (Tfloat)std::sqrt(re*re + im*im);
17881  if (val>vmax) val = vmax;
17882  if (val<vmin) val = vmin;
17883  *ptrd = (T)val;
17884  }
17885  } break;
17886  default :
17887  throw CImgArgumentException(_cimg_instance
17888  "noise(): Invalid specified noise type %d "
17889  "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
17890  cimg_instance,
17891  noise_type);
17892  }
17893  }
17894  return *this;
17895  }
17896 
17898  CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
17899  return (+*this).noise(sigma,noise_type);
17900  }
17901 
17903 
17913  CImg<T>& normalize(const T min_value, const T max_value) {
17914  if (is_empty()) return *this;
17915  const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
17916  T m, M = max_min(m);
17917  const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
17918  if (m==M) return fill(min_value);
17919  if (m!=a || M!=b) cimg_for(*this,ptrd,T) *ptrd = (T)((*ptrd-fm)/(fM-fm)*(b-a)+a);
17920  return *this;
17921  }
17922 
17924  CImg<Tfloat> get_normalize(const T min_value, const T max_value) const {
17925  return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value);
17926  }
17927 
17929 
17938  T *ptrd = _data;
17939  const unsigned long whd = (unsigned long)_width*_height*_depth;
17940  cimg_forXYZ(*this,x,y,z) {
17941  const T *ptrs = ptrd;
17942  float n = 0;
17943  cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
17944  n = (float)std::sqrt(n);
17945  T *_ptrd = ptrd++;
17946  if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
17947  else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
17948  }
17949  return *this;
17950  }
17951 
17954  return CImg<Tfloat>(*this,false).normalize();
17955  }
17956 
17958 
17967  CImg<T>& norm(const int norm_type=2) {
17968  if (_spectrum==1) return abs();
17969  return get_norm(norm_type).move_to(*this);
17970  }
17971 
17973  CImg<Tfloat> get_norm(const int norm_type=2) const {
17974  if (is_empty()) return *this;
17975  if (_spectrum==1) return get_abs();
17976  const T *ptrs = _data;
17977  const unsigned long whd = (unsigned long)_width*_height*_depth;
17978  CImg<Tfloat> res(_width,_height,_depth);
17979  Tfloat *ptrd = res._data;
17980  switch (norm_type) {
17981  case -1 : { // Linf norm
17982  cimg_forXYZ(*this,x,y,z) {
17983  Tfloat n = 0;
17984  const T *_ptrs = ptrs++;
17985  cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
17986  *(ptrd++) = n;
17987  }
17988  } break;
17989  case 1 : { // L1 norm
17990  cimg_forXYZ(*this,x,y,z) {
17991  Tfloat n = 0;
17992  const T *_ptrs = ptrs++;
17993  cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
17994  *(ptrd++) = n;
17995  }
17996  } break;
17997  default : { // L2 norm
17998  cimg_forXYZ(*this,x,y,z) {
17999  Tfloat n = 0;
18000  const T *_ptrs = ptrs++;
18001  cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
18002  *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
18003  }
18004  }
18005  }
18006  return res;
18007  }
18008 
18010 
18020  CImg<T>& cut(const T min_value, const T max_value) {
18021  if (is_empty()) return *this;
18022  const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
18023  cimg_for(*this,ptrd,T) *ptrd = (*ptrd<a)?a:((*ptrd>b)?b:*ptrd);
18024  return *this;
18025  }
18026 
18028  CImg<T> get_cut(const T min_value, const T max_value) const {
18029  return (+*this).cut(min_value,max_value);
18030  }
18031 
18033 
18043  CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
18044  if (!nb_levels)
18045  throw CImgArgumentException(_cimg_instance
18046  "quantize(): Invalid quantization request with 0 values.",
18047  cimg_instance);
18048 
18049  if (is_empty()) return *this;
18050  Tfloat m, M = (Tfloat)max_min(m), range = M - m;
18051  if (range>0) {
18052  if (keep_range) cimg_for(*this,ptrd,T) {
18053  const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
18054  *ptrd = (T)(m + cimg::min(val,nb_levels-1)*range/nb_levels);
18055  } else cimg_for(*this,ptrd,T) {
18056  const unsigned int val = (unsigned int)((*ptrd-m)*nb_levels/range);
18057  *ptrd = (T)cimg::min(val,nb_levels-1);
18058  }
18059  }
18060  return *this;
18061  }
18062 
18064  CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
18065  return (+*this).quantize(n,keep_range);
18066  }
18067 
18069 
18080  CImg<T>& threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) {
18081  if (is_empty()) return *this;
18082  if (strict_threshold) {
18083  if (soft_threshold) cimg_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>value?(T)(v-value):v<-(float)value?(T)(v+value):(T)0; }
18084  else cimg_for(*this,ptrd,T) *ptrd = *ptrd>value?(T)1:(T)0;
18085  } else {
18086  if (soft_threshold) cimg_for(*this,ptrd,T) { const T v = *ptrd; *ptrd = v>=value?(T)(v-value):v<=-(float)value?(T)(v+value):(T)0; }
18087  else cimg_for(*this,ptrd,T) *ptrd = *ptrd>=value?(T)1:(T)0;
18088  }
18089  return *this;
18090  }
18091 
18093  CImg<T> get_threshold(const T value, const bool soft_threshold=false, const bool strict_threshold=false) const {
18094  return (+*this).threshold(value,soft_threshold,strict_threshold);
18095  }
18096 
18098 
18114  CImg<T>& histogram(const unsigned int nb_levels, const T min_value=(T)0, const T max_value=(T)0) {
18115  return get_histogram(nb_levels,min_value,max_value).move_to(*this);
18116  }
18117 
18119  CImg<floatT> get_histogram(const unsigned int nb_levels, const T min_value=(T)0, const T max_value=(T)0) const {
18120  if (!nb_levels || is_empty()) return CImg<floatT>();
18121  T vmin = min_value<max_value?min_value:max_value, vmax = min_value<max_value?max_value:min_value;
18122  if (vmin==vmax && vmin==0) vmin = min_max(vmax);
18123  CImg<floatT> res(nb_levels,1,1,1,0);
18124  cimg_for(*this,ptrs,T) {
18125  const T val = *ptrs;
18126  if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels-1:(unsigned int)((val-vmin)*nb_levels/(vmax-vmin))];
18127  }
18128  return res;
18129  }
18130 
18132 
18146  CImg<T>& equalize(const unsigned int nb_levels, const T min_value=(T)0, const T max_value=(T)0) {
18147  if (is_empty()) return *this;
18148  T vmin = min_value, vmax = max_value;
18149  if (vmin==vmax && vmin==0) vmin = min_max(vmax);
18150  if (vmin<vmax) {
18151  CImg<floatT> hist = get_histogram(nb_levels,vmin,vmax);
18152  float cumul = 0;
18153  cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
18154  cimg_for(*this,ptrd,T) {
18155  const int pos = (unsigned int)((*ptrd-vmin)*(nb_levels-1)/(vmax-vmin));
18156  if (pos>=0 && pos<(int)nb_levels) *ptrd = (T)(vmin + (vmax-vmin)*hist[pos]/size());
18157  }
18158  }
18159  return *this;
18160  }
18161 
18163  CImg<T> get_equalize(const unsigned int nblevels, const T val_min=(T)0, const T val_max=(T)0) const {
18164  return (+*this).equalize(nblevels,val_min,val_max);
18165  }
18166 
18168 
18182  template<typename t>
18183  CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
18184  return get_index(colormap,dithering,map_indexes).move_to(*this);
18185  }
18186 
18188  template<typename t>
18190  get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
18191  if (colormap._spectrum!=_spectrum)
18192  throw CImgArgumentException(_cimg_instance
18193  "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
18194  "have incompatible dimensions.",
18195  cimg_instance,
18196  colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
18197 
18198  typedef typename CImg<t>::Tuint tuint;
18199  if (is_empty()) return CImg<tuint>();
18200  const unsigned long whd = (unsigned long)_width*_height*_depth, pwhd = (unsigned long)colormap._width*colormap._height*colormap._depth;
18201  CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
18202  tuint *ptrd = res._data;
18203  if (dithering>0) { // Dithered versions.
18204  const float ndithering = (dithering<0?0:dithering>1?1:dithering)/16;
18205  Tfloat valm = 0, valM = (Tfloat)max_min(valm);
18206  if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
18207  CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum-1);
18208  Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
18209  const unsigned long cwhd = (unsigned long)cache._width*cache._height*cache._depth;
18210  switch (_spectrum) {
18211  case 1 : { // Optimized for scalars.
18212  cimg_forYZ(*this,y,z) {
18213  if (y<height()-2) {
18214  Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y+1,z,0);
18215  cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
18216  }
18217  Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
18218  cimg_forX(*this,x) {
18219  const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
18220  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18221  for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
18222  const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
18223  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18224  }
18225  const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
18226  *ptrs0+=7*err0; *(ptrsn0-1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
18227  if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18228  }
18229  cimg::swap(cache_current,cache_next);
18230  }
18231  } break;
18232  case 2 : { // Optimized for 2d vectors.
18233  tuint *ptrd1 = ptrd + whd;
18234  cimg_forYZ(*this,y,z) {
18235  if (y<height()-2) {
18236  Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
18237  const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd;
18238  cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
18239  }
18240  Tfloat
18241  *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
18242  *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
18243  cimg_forX(*this,x) {
18244  const Tfloat
18245  _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
18246  _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
18247  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18248  for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18249  const Tfloat
18250  pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
18251  dist = pval0*pval0 + pval1*pval1;
18252  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18253  }
18254  const t *const ptrmin1 = ptrmin0 + pwhd;
18255  const Tfloat
18256  err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
18257  err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
18258  *ptrs0+=7*err0; *ptrs1+=7*err1;
18259  *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1;
18260  *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
18261  *ptrsn0+=err0; *ptrsn1+=err1;
18262  if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
18263  else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18264  }
18265  cimg::swap(cache_current,cache_next);
18266  }
18267  } break;
18268  case 3 : { // Optimized for 3d vectors (colors).
18269  tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
18270  cimg_forYZ(*this,y,z) {
18271  if (y<height()-2) {
18272  Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
18273  const T *ptrs0 = data(0,y+1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
18274  cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++); }
18275  }
18276  Tfloat
18277  *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
18278  *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
18279  cimg_forX(*this,x) {
18280  const Tfloat
18281  _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
18282  _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
18283  _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
18284  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18285  for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18286  const Tfloat
18287  pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, pval2 = (Tfloat)*(ptrp2++) - val2,
18288  dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
18289  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18290  }
18291  const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
18292  const Tfloat
18293  err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
18294  err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
18295  err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
18296 
18297  *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
18298  *(ptrsn0-1)+=3*err0; *(ptrsn1-1)+=3*err1; *(ptrsn2-1)+=3*err2;
18299  *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
18300  *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
18301 
18302  if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2; }
18303  else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18304  }
18305  cimg::swap(cache_current,cache_next);
18306  }
18307  } break;
18308  default : // Generic version
18309  cimg_forYZ(*this,y,z) {
18310  if (y<height()-2) {
18311  Tfloat *ptrc = cache_next;
18312  cimg_forC(*this,c) {
18313  Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y+1,z,c);
18314  cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
18315  ptrc+=cwhd;
18316  }
18317  }
18318  Tfloat *ptrs = cache_current, *ptrsn = cache_next;
18319  cimg_forX(*this,x) {
18320  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
18321  for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
18322  Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
18323  cimg_forC(*this,c) {
18324  const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
18325  dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
18326  }
18327  if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
18328  }
18329  const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++)-1;
18330  cimg_forC(*this,c) {
18331  const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
18332  *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
18333  _ptrmin+=pwhd; _ptrs+=cwhd-1; _ptrsn+=cwhd-2;
18334  }
18335  if (map_indexes) {
18336  tuint *_ptrd = ptrd++;
18337  cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
18338  }
18339  else *(ptrd++) = (tuint)(ptrmin - colormap._data);
18340  }
18341  cimg::swap(cache_current,cache_next);
18342  }
18343  }
18344  } else { // Non-dithered versions
18345  switch (_spectrum) {
18346  case 1 : { // Optimized for scalars.
18347  for (const T *ptrs0 = _data, *ptrs_end = ptrs0 + whd; ptrs0<ptrs_end; ) {
18348  const Tfloat val0 = (Tfloat)*(ptrs0++);
18349  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18350  for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
18351  const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
18352  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18353  }
18354  if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18355  }
18356  } break;
18357  case 2 : { // Optimized for 2d vectors.
18358  tuint *ptrd1 = ptrd + whd;
18359  for (const T *ptrs0 = _data, *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs1; ptrs0<ptrs_end; ) {
18360  const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
18361  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18362  for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18363  const Tfloat
18364  pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
18365  dist = pval0*pval0 + pval1*pval1;
18366  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18367  }
18368  if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
18369  else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18370  }
18371  } break;
18372  case 3 : { // Optimized for 3d vectors (colors).
18373  tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
18374  for (const T *ptrs0 = _data, *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd, *ptrs_end = ptrs1; ptrs0<ptrs_end; ) {
18375  const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
18376  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
18377  for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
18378  const Tfloat
18379  pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1, pval2 = (Tfloat)*(ptrp2++) - val2,
18380  dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
18381  if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
18382  }
18383  if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd); }
18384  else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
18385  }
18386  } break;
18387  default : // Generic version.
18388  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ++ptrs) {
18389  Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
18390  for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
18391  Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
18392  cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
18393  if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
18394  }
18395  if (map_indexes) {
18396  tuint *_ptrd = ptrd++;
18397  cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
18398  }
18399  else *(ptrd++) = (tuint)(ptrmin - colormap._data);
18400  }
18401  }
18402  }
18403  return res;
18404  }
18405 
18407 
18419  template<typename t>
18420  CImg<T>& map(const CImg<t>& colormap) {
18421  return get_map(colormap).move_to(*this);
18422  }
18423 
18425  template<typename t>
18426  CImg<t> get_map(const CImg<t>& colormap) const {
18427  if (_spectrum!=1 && colormap._spectrum!=1)
18428  throw CImgArgumentException(_cimg_instance
18429  "map(): Instance and specified colormap (%u,%u,%u,%u,%p) "
18430  "have incompatible dimensions.",
18431  cimg_instance,
18432  colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
18433 
18434  const unsigned long whd = (unsigned long)_width*_height*_depth, pwhd = (unsigned long)colormap._width*colormap._height*colormap._depth;
18435  CImg<t> res(_width,_height,_depth,colormap._spectrum==1?_spectrum:colormap._spectrum);
18436  switch (colormap._spectrum) {
18437  case 1 : { // Optimized for scalars.
18438  const T *ptrs = _data;
18439  cimg_for(res,ptrd,t) {
18440  const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind<pwhd?_ind:0;
18441  *ptrd = colormap[ind];
18442  }
18443  } break;
18444  case 2 : { // Optimized for 2d vectors.
18445  const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd;
18446  t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd;
18447  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
18448  const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind<pwhd?_ind:0;
18449  *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind];
18450  }
18451  } break;
18452  case 3 : { // Optimized for 3d vectors (colors).
18453  const t *const ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd;
18454  t *ptrd0 = res._data, *ptrd1 = ptrd0 + whd, *ptrd2 = ptrd1 + whd;
18455  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
18456  const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind<pwhd?_ind:0;
18457  *(ptrd0++) = ptrp0[ind]; *(ptrd1++) = ptrp1[ind]; *(ptrd2++) = ptrp2[ind];
18458  }
18459  } break;
18460  default : { // Generic version.
18461  t *ptrd = res._data;
18462  for (const T *ptrs = _data, *ptrs_end = ptrs + whd; ptrs<ptrs_end; ) {
18463  const unsigned long _ind = (unsigned long)*(ptrs++), ind = _ind<pwhd?_ind:0;
18464  const t *ptrp = colormap._data + ind;
18465  t *_ptrd = ptrd++; cimg_forC(res,c) { *_ptrd = *ptrp; _ptrd+=whd; ptrp+=pwhd; }
18466  }
18467  }
18468  }
18469  return res;
18470  }
18471 
18473 
18483  CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0) {
18484  return get_label(is_high_connectivity,tolerance).move_to(*this);
18485  }
18486 
18488  CImg<unsigned long> get_label(const bool is_high_connectivity=false,
18489  const Tfloat tolerance=0) const {
18490  if (is_empty()) return CImg<unsigned long>();
18491 
18492  // Create neighborhood tables.
18493  int dx[13], dy[13], dz[13], nb = 0;
18494  dx[nb]=1; dy[nb] = 0; dz[nb++]=0;
18495  dx[nb]=0; dy[nb] = 1; dz[nb++]=0;
18496  if (is_high_connectivity) {
18497  dx[nb]=1; dy[nb] = 1; dz[nb++]=0;
18498  dx[nb]=1; dy[nb] = -1; dz[nb++]=0;
18499  }
18500  if (_depth>1) { // 3d version.
18501  dx[nb]=0; dy[nb] = 0; dz[nb++]=1;
18502  if (is_high_connectivity) {
18503  dx[nb]=1; dy[nb] = 1; dz[nb++]=-1;
18504  dx[nb]=1; dy[nb] = 0; dz[nb++]=-1;
18505  dx[nb]=1; dy[nb] = -1; dz[nb++]=-1;
18506  dx[nb]=0; dy[nb] = 1; dz[nb++]=-1;
18507 
18508  dx[nb]=0; dy[nb] = 1; dz[nb++]=1;
18509  dx[nb]=1; dy[nb] = -1; dz[nb++]=1;
18510  dx[nb]=1; dy[nb] = 0; dz[nb++]=1;
18511  dx[nb]=1; dy[nb] = 1; dz[nb++]=1;
18512  }
18513  }
18514  return _get_label(nb,dx,dy,dz,tolerance);
18515  }
18516 
18518 
18522  template<typename t>
18523  CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0) {
18524  return get_label(connectivity_mask,tolerance).move_to(*this);
18525  }
18526 
18528  template<typename t>
18529  CImg<unsigned long> get_label(const CImg<t>& connectivity_mask,
18530  const Tfloat tolerance=0) const {
18531  int nb = 0;
18532  cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
18533  CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
18534  nb = 0;
18535  cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
18536  connectivity_mask(x,y,z)) {
18537  dx[nb] = x; dy[nb] = y; dz[nb++] = z;
18538  }
18539  return _get_label(nb,dx,dy,dz,tolerance);
18540  }
18541 
18542  CImg<unsigned long> _get_label(const unsigned int nb, const int
18543  *const dx, const int *const dy, const int *const dz,
18544  const Tfloat tolerance) const {
18545  CImg<unsigned long> res(_width,_height,_depth,_spectrum);
18546  cimg_forC(*this,c) {
18548 
18549  // Init label numbers.
18550  unsigned long *ptr = _res.data();
18551  cimg_foroff(_res,p) *(ptr++) = p;
18552 
18553  // For each neighbour-direction, label.
18554  for (unsigned int n = 0; n<nb; ++n) {
18555  const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
18556  if (_dx || _dy || _dz) {
18557  const int
18558  x0 = _dx<0?-_dx:0,
18559  x1 = _dx<0?_width:_width - _dx,
18560  y0 = _dy<0?-_dy:0,
18561  y1 = _dy<0?_height:_height - _dy,
18562  z0 = _dz<0?-_dz:0,
18563  z1 = _dz<0?_depth:_depth - _dz;
18564  const long wh = (long)_width*_height, offset = (long)_dz*wh + (long)_dy*_width + _dx;
18565  for (long z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
18566  for (long y = y0, ny = y0 + _dy, py = y0*_width + pz; y<y1; ++y, ++ny, py+=_width) {
18567  for (long x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
18568  if ((Tfloat)cimg::abs((*this)(x,y,z,c,wh)-(*this)(nx,ny,nz,c,wh))<=tolerance) {
18569  const long q = p + offset;
18570  unsigned long x, y;
18571  for (x = p<q?q:p, y = p<q?p:q; x!=y && _res[x]!=x; ) { x = _res[x]; if (x<y) cimg::swap(x,y); }
18572  if (x!=y) _res[x] = y;
18573  for (unsigned long _p = p; _p!=y; ) { const unsigned long h = _res[_p]; _res[_p] = y; _p = h; }
18574  for (unsigned long _q = q; _q!=y; ) { const unsigned long h = _res[_q]; _res[_q] = y; _q = h; }
18575  }
18576  }
18577  }
18578  }
18579  }
18580  }
18581 
18582  // Resolve equivalences.
18583  unsigned long counter = 0;
18584  ptr = _res.data();
18585  cimg_foroff(_res,p) { *ptr = *ptr==p?counter++:_res[*ptr]; ++ptr; }
18586  }
18587  return res;
18588  }
18589 
18591  //---------------------------------
18592  //
18594 
18595  //---------------------------------
18596 
18598 
18602  static const CImg<Tuchar>& default_LUT256() {
18603  static CImg<Tuchar> colormap;
18604  if (!colormap) {
18605  colormap.assign(1,256,1,3);
18606  for (unsigned int index = 0, r = 16; r<256; r+=32)
18607  for (unsigned int g = 16; g<256; g+=32)
18608  for (unsigned int b = 32; b<256; b+=64) {
18609  colormap(0,index,0) = (Tuchar)r;
18610  colormap(0,index,1) = (Tuchar)g;
18611  colormap(0,index++,2) = (Tuchar)b;
18612  }
18613  }
18614  return colormap;
18615  }
18616 
18618 
18622  static const CImg<Tuchar>& HSV_LUT256() {
18623  static CImg<Tuchar> colormap;
18624  if (!colormap) {
18625  CImg<Tint> tmp(1,256,1,3,1);
18626  tmp.get_shared_channel(0).sequence(0,359);
18627  colormap = tmp.HSVtoRGB();
18628  }
18629  return colormap;
18630  }
18631 
18633 
18637  static const CImg<Tuchar>& lines_LUT256() {
18638  static const unsigned char pal[] = {
18639  217,62,88,75,1,237,240,12,56,160,165,116,1,1,204,2,15,248,148,185,133,141,46,246,222,116,16,5,207,226,
18640  17,114,247,1,214,53,238,0,95,55,233,235,109,0,17,54,33,0,90,30,3,0,94,27,19,0,68,212,166,130,0,15,7,119,
18641  238,2,246,198,0,3,16,10,13,2,25,28,12,6,2,99,18,141,30,4,3,140,12,4,30,233,7,10,0,136,35,160,168,184,20,
18642  233,0,1,242,83,90,56,180,44,41,0,6,19,207,5,31,214,4,35,153,180,75,21,76,16,202,218,22,17,2,136,71,74,
18643  81,251,244,148,222,17,0,234,24,0,200,16,239,15,225,102,230,186,58,230,110,12,0,7,129,249,22,241,37,219,
18644  1,3,254,210,3,212,113,131,197,162,123,252,90,96,209,60,0,17,0,180,249,12,112,165,43,27,229,77,40,195,12,
18645  87,1,210,148,47,80,5,9,1,137,2,40,57,205,244,40,8,252,98,0,40,43,206,31,187,0,180,1,69,70,227,131,108,0,
18646  223,94,228,35,248,243,4,16,0,34,24,2,9,35,73,91,12,199,51,1,249,12,103,131,20,224,2,70,32,
18647  233,1,165,3,8,154,246,233,196,5,0,6,183,227,247,195,208,36,0,0,226,160,210,198,69,153,210,1,23,8,192,2,4,
18648  137,1,0,52,2,249,241,129,0,0,234,7,238,71,7,32,15,157,157,252,158,2,250,6,13,30,11,162,0,199,21,11,27,224,
18649  4,157,20,181,111,187,218,3,0,11,158,230,196,34,223,22,248,135,254,210,157,219,0,117,239,3,255,4,227,5,247,
18650  11,4,3,188,111,11,105,195,2,0,14,1,21,219,192,0,183,191,113,241,1,12,17,248,0,48,7,19,1,254,212,0,239,246,
18651  0,23,0,250,165,194,194,17,3,253,0,24,6,0,141,167,221,24,212,2,235,243,0,0,205,1,251,133,204,28,4,6,1,10,
18652  141,21,74,12,236,254,228,19,1,0,214,1,186,13,13,6,13,16,27,209,6,216,11,207,251,59,32,9,155,23,19,235,143,
18653  116,6,213,6,75,159,23,6,0,228,4,10,245,249,1,7,44,234,4,102,174,0,19,239,103,16,15,18,8,214,22,4,47,244,
18654  255,8,0,251,173,1,212,252,250,251,252,6,0,29,29,222,233,246,5,149,0,182,180,13,151,0,203,183,0,35,149,0,
18655  235,246,254,78,9,17,203,73,11,195,0,3,5,44,0,0,237,5,106,6,130,16,214,20,168,247,168,4,207,11,5,1,232,251,
18656  129,210,116,231,217,223,214,27,45,38,4,177,186,249,7,215,172,16,214,27,249,230,236,2,34,216,217,0,175,30,
18657  243,225,244,182,20,212,2,226,21,255,20,0,2,13,62,13,191,14,76,64,20,121,4,118,0,216,1,147,0,2,210,1,215,
18658  95,210,236,225,184,46,0,248,24,11,1,9,141,250,243,9,221,233,160,11,147,2,55,8,23,12,253,9,0,54,0,231,6,3,
18659  141,8,2,246,9,180,5,11,8,227,8,43,110,242,1,130,5,97,36,10,6,219,86,133,11,108,6,1,5,244,67,19,28,0,174,
18660  154,16,127,149,252,188,196,196,228,244,9,249,0,0,0,37,170,32,250,0,73,255,23,3,224,234,38,195,198,0,255,87,
18661  33,221,174,31,3,0,189,228,6,153,14,144,14,108,197,0,9,206,245,254,3,16,253,178,248,0,95,125,8,0,3,168,21,
18662  23,168,19,50,240,244,185,0,1,144,10,168,31,82,1,13 };
18663  static const CImg<Tuchar> colormap(pal,1,256,1,3,false);
18664  return colormap;
18665  }
18666 
18668 
18672  static const CImg<Tuchar>& hot_LUT256() {
18673  static CImg<Tuchar> colormap;
18674  if (!colormap) {
18675  colormap.assign(1,4,1,3,0);
18676  colormap[1] = colormap[2] = colormap[3] = colormap[6] = colormap[7] = colormap[11] = 255;
18677  colormap.resize(1,256,1,3,3);
18678  }
18679  return colormap;
18680  }
18681 
18683 
18687  static const CImg<Tuchar>& cool_LUT256() {
18688  static CImg<Tuchar> colormap;
18689  if (!colormap) colormap.assign(1,2,1,3).fill(0,255,255,0,255,255).resize(1,256,1,3,3);
18690  return colormap;
18691  }
18692 
18694 
18698  static const CImg<Tuchar>& jet_LUT256() {
18699  static CImg<Tuchar> colormap;
18700  if (!colormap) {
18701  colormap.assign(1,4,1,3,0);
18702  colormap[2] = colormap[3] = colormap[5] = colormap[6] = colormap[8] = colormap[9] = 255;
18703  colormap.resize(1,256,1,3,3);
18704  }
18705  return colormap;
18706  }
18707 
18709 
18713  static const CImg<Tuchar>& flag_LUT256() {
18714  static CImg<Tuchar> colormap;
18715  if (!colormap) {
18716  colormap.assign(1,4,1,3,0);
18717  colormap[0] = colormap[1] = colormap[5] = colormap[9] = colormap[10] = 255;
18718  colormap.resize(1,256,1,3,0,2);
18719  }
18720  return colormap;
18721  }
18722 
18724 
18728  static const CImg<Tuchar>& cube_LUT256() {
18729  static CImg<Tuchar> colormap;
18730  if (!colormap) {
18731  colormap.assign(1,8,1,3,0);
18732  colormap[1] = colormap[3] = colormap[5] = colormap[7] =
18733  colormap[10] = colormap[11] = colormap[12] = colormap[13] =
18734  colormap[20] = colormap[21] = colormap[22] = colormap[23] = 255;
18735  colormap.resize(1,256,1,3,3);
18736  }
18737  return colormap;
18738  }
18739 
18742  cimg_for(*this,ptr,T) {
18743  const Tfloat
18744  sval = (Tfloat)*ptr,
18745  nsval = (sval<0?0:sval>255?255:sval)/255,
18746  val = (Tfloat)(nsval<=0.04045f?nsval/12.92f:std::pow((nsval+0.055f)/(1.055f),2.4f));
18747  *ptr = (T)(val*255);
18748  }
18749  return *this;
18750  }
18751 
18754  return CImg<Tfloat>(*this,false).sRGBtoRGB();
18755  }
18756 
18759  cimg_for(*this,ptr,T) {
18760  const Tfloat
18761  val = (Tfloat)*ptr,
18762  nval = (val<0?0:val>255?255:val)/255,
18763  sval = (Tfloat)(nval<=0.0031308f?nval*12.92f:1.055f*std::pow(nval,0.416667f)-0.055f);
18764  *ptr = (T)(sval*255);
18765  }
18766  return *this;
18767  }
18768 
18771  return CImg<Tfloat>(*this,false).RGBtosRGB();
18772  }
18773 
18776  if (_spectrum!=3)
18777  throw CImgInstanceException(_cimg_instance
18778  "RGBtoHSV(): Instance is not a RGB image.",
18779  cimg_instance);
18780 
18781  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18782  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18783  const Tfloat
18784  R = (Tfloat)*p1,
18785  G = (Tfloat)*p2,
18786  B = (Tfloat)*p3,
18787  nR = (R<0?0:(R>255?255:R))/255,
18788  nG = (G<0?0:(G>255?255:G))/255,
18789  nB = (B<0?0:(B>255?255:B))/255,
18790  m = cimg::min(nR,nG,nB),
18791  M = cimg::max(nR,nG,nB);
18792  Tfloat H = 0, S = 0;
18793  if (M!=m) {
18794  const Tfloat
18795  f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
18796  i = (Tfloat)((nR==m)?3:((nG==m)?5:1));
18797  H = (i-f/(M-m));
18798  if (H>=6) H-=6;
18799  H*=60;
18800  S = (M-m)/M;
18801  }
18802  *(p1++) = (T)H;
18803  *(p2++) = (T)S;
18804  *(p3++) = (T)M;
18805  }
18806  return *this;
18807  }
18808 
18811  return CImg<Tfloat>(*this,false).RGBtoHSV();
18812  }
18813 
18816  if (_spectrum!=3)
18817  throw CImgInstanceException(_cimg_instance
18818  "HSVtoRGB(): Instance is not a HSV image.",
18819  cimg_instance);
18820 
18821  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18822  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18823  Tfloat
18824  H = (Tfloat)*p1,
18825  S = (Tfloat)*p2,
18826  V = (Tfloat)*p3,
18827  R = 0, G = 0, B = 0;
18828  if (H==0 && S==0) R = G = B = V;
18829  else {
18830  H/=60;
18831  const int i = (int)std::floor(H);
18832  const Tfloat
18833  f = (i&1)?(H - i):(1 - H + i),
18834  m = V*(1 - S),
18835  n = V*(1 - S*f);
18836  switch (i) {
18837  case 6 :
18838  case 0 : R = V; G = n; B = m; break;
18839  case 1 : R = n; G = V; B = m; break;
18840  case 2 : R = m; G = V; B = n; break;
18841  case 3 : R = m; G = n; B = V; break;
18842  case 4 : R = n; G = m; B = V; break;
18843  case 5 : R = V; G = m; B = n; break;
18844  }
18845  }
18846  R*=255; G*=255; B*=255;
18847  *(p1++) = (T)(R<0?0:(R>255?255:R));
18848  *(p2++) = (T)(G<0?0:(G>255?255:G));
18849  *(p3++) = (T)(B<0?0:(B>255?255:B));
18850  }
18851  return *this;
18852  }
18853 
18856  return CImg<Tuchar>(*this,false).HSVtoRGB();
18857  }
18858 
18861  if (_spectrum!=3)
18862  throw CImgInstanceException(_cimg_instance
18863  "RGBtoHSL(): Instance is not a RGB image.",
18864  cimg_instance);
18865 
18866  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18867  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18868  const Tfloat
18869  R = (Tfloat)*p1,
18870  G = (Tfloat)*p2,
18871  B = (Tfloat)*p3,
18872  nR = (R<0?0:(R>255?255:R))/255,
18873  nG = (G<0?0:(G>255?255:G))/255,
18874  nB = (B<0?0:(B>255?255:B))/255,
18875  m = cimg::min(nR,nG,nB),
18876  M = cimg::max(nR,nG,nB),
18877  L = (m + M)/2;
18878  Tfloat H = 0, S = 0;
18879  if (M==m) H = S = 0;
18880  else {
18881  const Tfloat
18882  f = (nR==m)?(nG-nB):((nG==m)?(nB-nR):(nR-nG)),
18883  i = (nR==m)?3.0f:((nG==m)?5.0f:1.0f);
18884  H = (i-f/(M-m));
18885  if (H>=6) H-=6;
18886  H*=60;
18887  S = (2*L<=1)?((M-m)/(M+m)):((M-m)/(2-M-m));
18888  }
18889  *(p1++) = (T)H;
18890  *(p2++) = (T)S;
18891  *(p3++) = (T)L;
18892  }
18893  return *this;
18894  }
18895 
18898  return CImg< Tfloat>(*this,false).RGBtoHSL();
18899  }
18900 
18903  if (_spectrum!=3)
18904  throw CImgInstanceException(_cimg_instance
18905  "HSLtoRGB(): Instance is not a HSL image.",
18906  cimg_instance);
18907 
18908  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18909  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18910  const Tfloat
18911  H = (Tfloat)*p1,
18912  S = (Tfloat)*p2,
18913  L = (Tfloat)*p3,
18914  q = 2*L<1?L*(1+S):(L+S-L*S),
18915  p = 2*L-q,
18916  h = H/360,
18917  tr = h + 1.0f/3,
18918  tg = h,
18919  tb = h - 1.0f/3,
18920  ntr = tr<0?tr+1:(tr>1?tr-1:tr),
18921  ntg = tg<0?tg+1:(tg>1?tg-1:tg),
18922  ntb = tb<0?tb+1:(tb>1?tb-1:tb),
18923  R = 255*(6*ntr<1?p+(q-p)*6*ntr:(2*ntr<1?q:(3*ntr<2?p+(q-p)*6*(2.0f/3-ntr):p))),
18924  G = 255*(6*ntg<1?p+(q-p)*6*ntg:(2*ntg<1?q:(3*ntg<2?p+(q-p)*6*(2.0f/3-ntg):p))),
18925  B = 255*(6*ntb<1?p+(q-p)*6*ntb:(2*ntb<1?q:(3*ntb<2?p+(q-p)*6*(2.0f/3-ntb):p)));
18926  *(p1++) = (T)(R<0?0:(R>255?255:R));
18927  *(p2++) = (T)(G<0?0:(G>255?255:G));
18928  *(p3++) = (T)(B<0?0:(B>255?255:B));
18929  }
18930  return *this;
18931  }
18932 
18935  return CImg<Tuchar>(*this,false).HSLtoRGB();
18936  }
18937 
18940  if (_spectrum!=3)
18941  throw CImgInstanceException(_cimg_instance
18942  "RGBtoHSI(): Instance is not a RGB image.",
18943  cimg_instance);
18944 
18945  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18946  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18947  const Tfloat
18948  R = (Tfloat)*p1,
18949  G = (Tfloat)*p2,
18950  B = (Tfloat)*p3,
18951  nR = (R<0?0:(R>255?255:R))/255,
18952  nG = (G<0?0:(G>255?255:G))/255,
18953  nB = (B<0?0:(B>255?255:B))/255,
18954  m = cimg::min(nR,nG,nB),
18955  theta = (Tfloat)(std::acos(0.5f*((nR-nG)+(nR-nB))/std::sqrt(std::pow(nR-nG,2)+(nR-nB)*(nG-nB)))*180/cimg::PI),
18956  sum = nR + nG + nB;
18957  Tfloat H = 0, S = 0, I = 0;
18958  if (theta>0) H = (nB<=nG)?theta:360-theta;
18959  if (sum>0) S = 1 - 3/sum*m;
18960  I = sum/3;
18961  *(p1++) = (T)H;
18962  *(p2++) = (T)S;
18963  *(p3++) = (T)I;
18964  }
18965  return *this;
18966  }
18967 
18970  return CImg<Tfloat>(*this,false).RGBtoHSI();
18971  }
18972 
18975  if (_spectrum!=3)
18976  throw CImgInstanceException(_cimg_instance
18977  "HSItoRGB(): Instance is not a HSI image.",
18978  cimg_instance);
18979 
18980  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
18981  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
18982  Tfloat
18983  H = (Tfloat)*p1,
18984  S = (Tfloat)*p2,
18985  I = (Tfloat)*p3,
18986  a = I*(1-S),
18987  R = 0, G = 0, B = 0;
18988  if (H<120) {
18989  B = a;
18990  R = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
18991  G = 3*I-(R+B);
18992  } else if (H<240) {
18993  H-=120;
18994  R = a;
18995  G = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
18996  B = 3*I-(R+G);
18997  } else {
18998  H-=240;
18999  G = a;
19000  B = (Tfloat)(I*(1+S*std::cos(H*cimg::PI/180)/std::cos((60-H)*cimg::PI/180)));
19001  R = 3*I-(G+B);
19002  }
19003  R*=255; G*=255; B*=255;
19004  *(p1++) = (T)(R<0?0:(R>255?255:R));
19005  *(p2++) = (T)(G<0?0:(G>255?255:G));
19006  *(p3++) = (T)(B<0?0:(B>255?255:B));
19007  }
19008  return *this;
19009  }
19010 
19013  return CImg< Tuchar>(*this,false).HSItoRGB();
19014  }
19015 
19018  if (_spectrum!=3)
19019  throw CImgInstanceException(_cimg_instance
19020  "RGBtoYCbCr(): Instance is not a RGB image.",
19021  cimg_instance);
19022 
19023  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19024  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19025  const Tfloat
19026  R = (Tfloat)*p1,
19027  G = (Tfloat)*p2,
19028  B = (Tfloat)*p3,
19029  Y = (66*R + 129*G + 25*B + 128)/256 + 16,
19030  Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
19031  Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
19032  *(p1++) = (T)(Y<0?0:(Y>255?255:Y));
19033  *(p2++) = (T)(Cb<0?0:(Cb>255?255:Cb));
19034  *(p3++) = (T)(Cr<0?0:(Cr>255?255:Cr));
19035  }
19036  return *this;
19037  }
19038 
19041  return CImg<Tuchar>(*this,false).RGBtoYCbCr();
19042  }
19043 
19046  if (_spectrum!=3)
19047  throw CImgInstanceException(_cimg_instance
19048  "YCbCrtoRGB(): Instance is not a YCbCr image.",
19049  cimg_instance);
19050 
19051  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19052  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19053  const Tfloat
19054  Y = (Tfloat)*p1 - 16,
19055  Cb = (Tfloat)*p2 - 128,
19056  Cr = (Tfloat)*p3 - 128,
19057  R = (298*Y + 409*Cr + 128)/256,
19058  G = (298*Y - 100*Cb - 208*Cr + 128)/256,
19059  B = (298*Y + 516*Cb + 128)/256;
19060  *(p1++) = (T)(R<0?0:(R>255?255:R));
19061  *(p2++) = (T)(G<0?0:(G>255?255:G));
19062  *(p3++) = (T)(B<0?0:(B>255?255:B));
19063  }
19064  return *this;
19065  }
19066 
19069  return CImg<Tuchar>(*this,false).YCbCrtoRGB();
19070  }
19071 
19074  if (_spectrum!=3)
19075  throw CImgInstanceException(_cimg_instance
19076  "RGBtoYUV(): Instance is not a RGB image.",
19077  cimg_instance);
19078 
19079  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19080  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19081  const Tfloat
19082  R = (Tfloat)*p1/255,
19083  G = (Tfloat)*p2/255,
19084  B = (Tfloat)*p3/255,
19085  Y = 0.299f*R + 0.587f*G + 0.114f*B;
19086  *(p1++) = (T)Y;
19087  *(p2++) = (T)(0.492f*(B-Y));
19088  *(p3++) = (T)(0.877*(R-Y));
19089  }
19090  return *this;
19091  }
19092 
19095  return CImg<Tfloat>(*this,false).RGBtoYUV();
19096  }
19097 
19100  if (_spectrum!=3)
19101  throw CImgInstanceException(_cimg_instance
19102  "YUVtoRGB(): Instance is not a YUV image.",
19103  cimg_instance);
19104 
19105  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19106  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19107  const Tfloat
19108  Y = (Tfloat)*p1,
19109  U = (Tfloat)*p2,
19110  V = (Tfloat)*p3,
19111  R = (Y + 1.140f*V)*255,
19112  G = (Y - 0.395f*U - 0.581f*V)*255,
19113  B = (Y + 2.032f*U)*255;
19114  *(p1++) = (T)(R<0?0:(R>255?255:R));
19115  *(p2++) = (T)(G<0?0:(G>255?255:G));
19116  *(p3++) = (T)(B<0?0:(B>255?255:B));
19117  }
19118  return *this;
19119  }
19120 
19123  return CImg< Tuchar>(*this,false).YUVtoRGB();
19124  }
19125 
19128  if (_spectrum!=3)
19129  throw CImgInstanceException(_cimg_instance
19130  "RGBtoCMY(): Instance is not a RGB image.",
19131  cimg_instance);
19132 
19133  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19134  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19135  const Tfloat
19136  R = (Tfloat)*p1,
19137  G = (Tfloat)*p2,
19138  B = (Tfloat)*p3,
19139  C = 255 - R,
19140  M = 255 - G,
19141  Y = 255 - B;
19142  *(p1++) = (T)(C<0?0:(C>255?255:C));
19143  *(p2++) = (T)(M<0?0:(M>255?255:M));
19144  *(p3++) = (T)(Y<0?0:(Y>255?255:Y));
19145  }
19146  return *this;
19147  }
19148 
19151  return CImg<Tfloat>(*this,false).RGBtoCMY();
19152  }
19153 
19156  if (_spectrum!=3)
19157  throw CImgInstanceException(_cimg_instance
19158  "CMYtoRGB(): Instance is not a CMY image.",
19159  cimg_instance);
19160 
19161  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19162  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19163  const Tfloat
19164  C = (Tfloat)*p1,
19165  M = (Tfloat)*p2,
19166  Y = (Tfloat)*p3,
19167  R = 255 - C,
19168  G = 255 - M,
19169  B = 255 - Y;
19170  *(p1++) = (T)(R<0?0:(R>255?255:R));
19171  *(p2++) = (T)(G<0?0:(G>255?255:G));
19172  *(p3++) = (T)(B<0?0:(B>255?255:B));
19173  }
19174  return *this;
19175  }
19176 
19179  return CImg<Tuchar>(*this,false).CMYtoRGB();
19180  }
19181 
19184  return get_CMYtoCMYK().move_to(*this);
19185  }
19186 
19189  if (_spectrum!=3)
19190  throw CImgInstanceException(_cimg_instance
19191  "CMYtoCMYK(): Instance is not a CMY image.",
19192  cimg_instance);
19193 
19194  CImg<Tfloat> res(_width,_height,_depth,4);
19195  const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
19196  Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2), *pd4 = res.data(0,0,0,3);
19197  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19198  Tfloat
19199  C = (Tfloat)*(ps1++),
19200  M = (Tfloat)*(ps2++),
19201  Y = (Tfloat)*(ps3++),
19202  K = cimg::min(C,M,Y);
19203  if (K>=255) C = M = Y = 0;
19204  else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
19205  *(pd1++) = (Tfloat)(C<0?0:(C>255?255:C));
19206  *(pd2++) = (Tfloat)(M<0?0:(M>255?255:M));
19207  *(pd3++) = (Tfloat)(Y<0?0:(Y>255?255:Y));
19208  *(pd4++) = (Tfloat)(K<0?0:(K>255?255:K));
19209  }
19210  return res;
19211  }
19212 
19215  return get_CMYKtoCMY().move_to(*this);
19216  }
19217 
19220  if (_spectrum!=4)
19221  throw CImgInstanceException(_cimg_instance
19222  "CMYKtoCMY(): Instance is not a CMYK image.",
19223  cimg_instance);
19224 
19225  CImg<Tfloat> res(_width,_height,_depth,3);
19226  const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2), *ps4 = data(0,0,0,3);
19227  Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
19228  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19229  const Tfloat
19230  C = (Tfloat)*(ps1++),
19231  M = (Tfloat)*(ps2++),
19232  Y = (Tfloat)*(ps3++),
19233  K = (Tfloat)*(ps4++),
19234  K1 = 1 - K/255,
19235  nC = C*K1 + K,
19236  nM = M*K1 + K,
19237  nY = Y*K1 + K;
19238  *(pd1++) = (Tfloat)(nC<0?0:(nC>255?255:nC));
19239  *(pd2++) = (Tfloat)(nM<0?0:(nM>255?255:nM));
19240  *(pd3++) = (Tfloat)(nY<0?0:(nY>255?255:nY));
19241  }
19242  return res;
19243  }
19244 
19246 
19250  if (_spectrum!=3)
19251  throw CImgInstanceException(_cimg_instance
19252  "RGBtoXYZ(): Instance is not a RGB image.",
19253  cimg_instance);
19254 
19255  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19256  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19257  const Tfloat
19258  R = (Tfloat)*p1/255,
19259  G = (Tfloat)*p2/255,
19260  B = (Tfloat)*p3/255;
19261  *(p1++) = (T)(0.412453f*R + 0.357580f*G + 0.180423f*B);
19262  *(p2++) = (T)(0.212671f*R + 0.715160f*G + 0.072169f*B);
19263  *(p3++) = (T)(0.019334f*R + 0.119193f*G + 0.950227f*B);
19264  }
19265  return *this;
19266  }
19267 
19270  return CImg<Tfloat>(*this,false).RGBtoXYZ();
19271  }
19272 
19275  if (_spectrum!=3)
19276  throw CImgInstanceException(_cimg_instance
19277  "XYZtoRGB(): Instance is not a XYZ image.",
19278  cimg_instance);
19279 
19280  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19281  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19282  const Tfloat
19283  X = (Tfloat)*p1*255,
19284  Y = (Tfloat)*p2*255,
19285  Z = (Tfloat)*p3*255,
19286  R = 3.240479f*X - 1.537150f*Y - 0.498535f*Z,
19287  G = -0.969256f*X + 1.875992f*Y + 0.041556f*Z,
19288  B = 0.055648f*X - 0.204043f*Y + 1.057311f*Z;
19289  *(p1++) = (T)(R<0?0:(R>255?255:R));
19290  *(p2++) = (T)(G<0?0:(G>255?255:G));
19291  *(p3++) = (T)(B<0?0:(B>255?255:B));
19292  }
19293  return *this;
19294  }
19295 
19298  return CImg<Tuchar>(*this,false).XYZtoRGB();
19299  }
19300 
19303 #define _cimg_Labf(x) ((x)>=0.008856f?(std::pow(x,(Tfloat)1/3)):(7.787f*(x)+16.0f/116))
19304 
19305  if (_spectrum!=3)
19306  throw CImgInstanceException(_cimg_instance
19307  "XYZtoLab(): Instance is not a XYZ image.",
19308  cimg_instance);
19309 
19310  const Tfloat
19311  Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
19312  Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
19313  Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
19314  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19315  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19316  const Tfloat
19317  X = (Tfloat)*p1,
19318  Y = (Tfloat)*p2,
19319  Z = (Tfloat)*p3,
19320  XXn = X/Xn, YYn = Y/Yn, ZZn = Z/Zn,
19321  fX = (Tfloat)_cimg_Labf(XXn),
19322  fY = (Tfloat)_cimg_Labf(YYn),
19323  fZ = (Tfloat)_cimg_Labf(ZZn);
19324  *(p1++) = (T)cimg::max(0.0f,116*fY - 16);
19325  *(p2++) = (T)(500*(fX - fY));
19326  *(p3++) = (T)(200*(fY - fZ));
19327  }
19328  return *this;
19329  }
19330 
19333  return CImg<Tfloat>(*this,false).XYZtoLab();
19334  }
19335 
19338 #define _cimg_Labfi(x) ((x)>=0.206893f?((x)*(x)*(x)):(((x)-16.0f/116)/7.787f))
19339 
19340  if (_spectrum!=3)
19341  throw CImgInstanceException(_cimg_instance
19342  "LabtoXYZ(): Instance is not a Lab image.",
19343  cimg_instance);
19344 
19345  const Tfloat
19346  Xn = (Tfloat)(0.412453f + 0.357580f + 0.180423f),
19347  Yn = (Tfloat)(0.212671f + 0.715160f + 0.072169f),
19348  Zn = (Tfloat)(0.019334f + 0.119193f + 0.950227f);
19349  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19350  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19351  const Tfloat
19352  L = (Tfloat)*p1,
19353  a = (Tfloat)*p2,
19354  b = (Tfloat)*p3,
19355  cY = (L + 16)/116,
19356  Y = (Tfloat)(Yn*_cimg_Labfi(cY)),
19357  pY = (Tfloat)std::pow(Y/Yn,(Tfloat)1/3),
19358  cX = a/500 + pY,
19359  X = Xn*cX*cX*cX,
19360  cZ = pY - b/200,
19361  Z = Zn*cZ*cZ*cZ;
19362  *(p1++) = (T)(X);
19363  *(p2++) = (T)(Y);
19364  *(p3++) = (T)(Z);
19365  }
19366  return *this;
19367  }
19368 
19371  return CImg<Tfloat>(*this,false).LabtoXYZ();
19372  }
19373 
19376  if (_spectrum!=3)
19377  throw CImgInstanceException(_cimg_instance
19378  "XYZtoxyY(): Instance is not a XYZ image.",
19379  cimg_instance);
19380 
19381  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19382  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19383  const Tfloat
19384  X = (Tfloat)*p1,
19385  Y = (Tfloat)*p2,
19386  Z = (Tfloat)*p3,
19387  sum = (X+Y+Z),
19388  nsum = sum>0?sum:1;
19389  *(p1++) = (T)(X/nsum);
19390  *(p2++) = (T)(Y/nsum);
19391  *(p3++) = (T)Y;
19392  }
19393  return *this;
19394  }
19395 
19398  return CImg<Tfloat>(*this,false).XYZtoxyY();
19399  }
19400 
19403  if (_spectrum!=3)
19404  throw CImgInstanceException(_cimg_instance
19405  "xyYtoXYZ(): Instance is not a xyY image.",
19406  cimg_instance);
19407 
19408  T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
19409  for (unsigned long N = (unsigned long)_width*_height*_depth; N; --N) {
19410  const Tfloat
19411  px = (Tfloat)*p1,
19412  py = (Tfloat)*p2,
19413  Y = (Tfloat)*p3,
19414  ny = py>0?py:1;
19415  *(p1++) = (T)(px*Y/ny);
19416  *(p2++) = (T)Y;
19417  *(p3++) = (T)((1-px-py)*Y/ny);
19418  }
19419  return *this;
19420  }
19421 
19424  return CImg<Tfloat>(*this,false).xyYtoXYZ();
19425  }
19426 
19429  return RGBtoXYZ().XYZtoLab();
19430  }
19431 
19434  return CImg<Tfloat>(*this,false).RGBtoLab();
19435  }
19436 
19439  return LabtoXYZ().XYZtoRGB();
19440  }
19441 
19444  return CImg<Tuchar>(*this,false).LabtoRGB();
19445  }
19446 
19449  return RGBtoXYZ().XYZtoxyY();
19450  }
19451 
19454  return CImg<Tfloat>(*this,false).RGBtoxyY();
19455  }
19456 
19459  return xyYtoXYZ().XYZtoRGB();
19460  }
19461 
19464  return CImg<Tuchar>(*this,false).xyYtoRGB();
19465  }
19466 
19469  return RGBtoCMY().CMYtoCMYK();
19470  }
19471 
19474  return CImg<Tfloat>(*this,false).RGBtoCMYK();
19475  }
19476 
19479  return CMYKtoCMY().CMYtoRGB();
19480  }
19481 
19484  return CImg<Tuchar>(*this,false).CMYKtoRGB();
19485  }
19486 
19488 
19492  return get_RGBtoBayer().move_to(*this);
19493  }
19494 
19497  if (_spectrum!=3)
19498  throw CImgInstanceException(_cimg_instance
19499  "RGBtoBayer(): Instance is not a RGB image.",
19500  cimg_instance);
19501 
19502  CImg<T> res(_width,_height,_depth,1);
19503  const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
19504  T *ptrd = res._data;
19505  cimg_forXYZ(*this,x,y,z) {
19506  if (y%2) {
19507  if (x%2) *(ptrd++) = *ptr_b;
19508  else *(ptrd++) = *ptr_g;
19509  } else {
19510  if (x%2) *(ptrd++) = *ptr_g;
19511  else *(ptrd++) = *ptr_r;
19512  }
19513  ++ptr_r; ++ptr_g; ++ptr_b;
19514  }
19515  return res;
19516  }
19517 
19519  CImg<T>& BayertoRGB(const unsigned int interpolation_type=3) {
19520  return get_BayertoRGB(interpolation_type).move_to(*this);
19521  }
19522 
19524  CImg<Tuchar> get_BayertoRGB(const unsigned int interpolation_type=3) const {
19525  if (_spectrum!=1)
19526  throw CImgInstanceException(_cimg_instance
19527  "BayertoRGB(): Instance is not a Bayer image.",
19528  cimg_instance);
19529 
19530  CImg<Tuchar> res(_width,_height,_depth,3);
19531  CImg_3x3(I,T);
19532  Tuchar *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2);
19533  switch (interpolation_type) {
19534  case 3 : { // Edge-directed
19535  CImg_3x3(R,T);
19536  CImg_3x3(G,T);
19537  CImg_3x3(B,T);
19538  cimg_forXYZ(*this,x,y,z) {
19539  const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19540  cimg_get3x3(*this,x,y,z,0,I,T);
19541  if (y%2) {
19542  if (x%2) {
19543  const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
19544  *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
19545  } else *ptr_g = (Tuchar)Icc;
19546  } else {
19547  if (x%2) *ptr_g = (Tuchar)Icc;
19548  else {
19549  const Tfloat alpha = cimg::sqr((Tfloat)Inc - Ipc), beta = cimg::sqr((Tfloat)Icn - Icp), cx = 1/(1+alpha), cy = 1/(1+beta);
19550  *ptr_g = (Tuchar)((cx*(Inc+Ipc) + cy*(Icn+Icp))/(2*(cx+cy)));
19551  }
19552  }
19553  ++ptr_g;
19554  }
19555  cimg_forXYZ(*this,x,y,z) {
19556  const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19557  cimg_get3x3(*this,x,y,z,0,I,T);
19558  cimg_get3x3(res,x,y,z,1,G,T);
19559  if (y%2) {
19560  if (x%2) *ptr_b = (Tuchar)Icc;
19561  else { *ptr_r = (Tuchar)((Icn+Icp)/2); *ptr_b = (Tuchar)((Inc+Ipc)/2); }
19562  } else {
19563  if (x%2) { *ptr_r = (Tuchar)((Inc+Ipc)/2); *ptr_b = (Tuchar)((Icn+Icp)/2); }
19564  else *ptr_r = (Tuchar)Icc;
19565  }
19566  ++ptr_r; ++ptr_b;
19567  }
19568  ptr_r = res.data(0,0,0,0);
19569  ptr_g = res.data(0,0,0,1);
19570  ptr_b = res.data(0,0,0,2);
19571  cimg_forXYZ(*this,x,y,z) {
19572  const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19573  cimg_get3x3(res,x,y,z,0,R,T);
19574  cimg_get3x3(res,x,y,z,1,G,T);
19575  cimg_get3x3(res,x,y,z,2,B,T);
19576  if (y%2) {
19577  if (x%2) {
19578  const float alpha = (float)cimg::sqr(Rnc-Rpc), beta = (float)cimg::sqr(Rcn-Rcp), cx = 1/(1+alpha), cy = 1/(1+beta);
19579  *ptr_r = (Tuchar)((cx*(Rnc+Rpc) + cy*(Rcn+Rcp))/(2*(cx+cy)));
19580  }
19581  } else {
19582  if (!(x%2)) {
19583  const float alpha = (float)cimg::sqr(Bnc-Bpc), beta = (float)cimg::sqr(Bcn-Bcp), cx = 1/(1+alpha), cy = 1/(1+beta);
19584  *ptr_b = (Tuchar)((cx*(Bnc+Bpc) + cy*(Bcn+Bcp))/(2*(cx+cy)));
19585  }
19586  }
19587  ++ptr_r; ++ptr_g; ++ptr_b;
19588  }
19589  } break;
19590  case 2 : { // Linear interpolation
19591  cimg_forXYZ(*this,x,y,z) {
19592  const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19593  cimg_get3x3(*this,x,y,z,0,I,T);
19594  if (y%2) {
19595  if (x%2) { *ptr_r = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)Icc; }
19596  else { *ptr_r = (Tuchar)((Icp+Icn)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Inc+Ipc)/2); }
19597  } else {
19598  if (x%2) { *ptr_r = (Tuchar)((Ipc+Inc)/2); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)((Icn+Icp)/2); }
19599  else { *ptr_r = (Tuchar)Icc; *ptr_g = (Tuchar)((Inc+Ipc+Icn+Icp)/4); *ptr_b = (Tuchar)((Ipp+Inn+Ipn+Inp)/4); }
19600  }
19601  ++ptr_r; ++ptr_g; ++ptr_b;
19602  }
19603  } break;
19604  case 1 : { // Nearest neighbor interpolation
19605  cimg_forXYZ(*this,x,y,z) {
19606  const int _p1x = x?x-1:1, _p1y = y?y-1:1, _n1x = x<width()-1?x+1:x-1, _n1y = y<height()-1?y+1:y-1;
19607  cimg_get3x3(*this,x,y,z,0,I,T);
19608  if (y%2) {
19609  if (x%2) { *ptr_r = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp); *ptr_g = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp); *ptr_b = (Tuchar)Icc; }
19610  else { *ptr_r = (Tuchar)cimg::min(Icn,Icp); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Inc,Ipc); }
19611  } else {
19612  if (x%2) { *ptr_r = (Tuchar)cimg::min(Inc,Ipc); *ptr_g = (Tuchar)Icc; *ptr_b = (Tuchar)cimg::min(Icn,Icp); }
19613  else { *ptr_r = (Tuchar)Icc; *ptr_g = (Tuchar)cimg::min(Inc,Ipc,Icn,Icp); *ptr_b = (Tuchar)cimg::min(Ipp,Inn,Ipn,Inp); }
19614  }
19615  ++ptr_r; ++ptr_g; ++ptr_b;
19616  }
19617  } break;
19618  default : { // 0-filling interpolation
19619  const T *ptrs = _data;
19620  res.fill(0);
19621  cimg_forXYZ(*this,x,y,z) {
19622  const T val = *(ptrs++);
19623  if (y%2) { if (x%2) *ptr_b = val; else *ptr_g = val; } else { if (x%2) *ptr_g = val; else *ptr_r = val; }
19624  ++ptr_r; ++ptr_g; ++ptr_b;
19625  }
19626  }
19627  }
19628  return res;
19629  }
19630 
19632  //------------------------------------------
19633  //
19635 
19636  //------------------------------------------
19637 
19638  static float _cimg_lanczos(const float x) {
19639  if (x<=-2 || x>=2) return 0;
19640  const float a = (float)cimg::PI*x, b = 0.5f*a;
19641  return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
19642  }
19643 
19645 
19666  CImg<T>& resize(const int size_x, const int size_y=-100,
19667  const int size_z=-100, const int size_c=-100,
19668  const int interpolation_type=1, const unsigned int boundary_conditions=0,
19669  const float centering_x = 0, const float centering_y = 0,
19670  const float centering_z = 0, const float centering_c = 0) {
19671  if (!size_x || !size_y || !size_z || !size_c) return assign();
19672  const unsigned int
19673  _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
19674  _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
19675  _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
19676  _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
19677  sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
19678  if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
19679  if (is_empty()) return assign(sx,sy,sz,sc,0);
19680  if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
19681  _width = sx; _height = sy; _depth = sz; _spectrum = sc;
19682  return *this;
19683  }
19684  return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,centering_x,centering_y,centering_z,centering_c).move_to(*this);
19685  }
19686 
19688  CImg<T> get_resize(const int size_x, const int size_y = -100,
19689  const int size_z = -100, const int size_c = -100,
19690  const int interpolation_type=1, const unsigned int boundary_conditions=0,
19691  const float centering_x = 0, const float centering_y = 0,
19692  const float centering_z = 0, const float centering_c = 0) const {
19693  if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
19694  centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
19695  throw CImgArgumentException(_cimg_instance
19696  "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
19697  cimg_instance,
19698  centering_x,centering_y,centering_z,centering_c);
19699 
19700  if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
19701  const unsigned int
19702  _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
19703  _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
19704  _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
19705  _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
19706  sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
19707  if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
19708  if (is_empty()) return CImg<T>(sx,sy,sz,sc,0);
19709 
19710  CImg<T> res;
19711  switch (interpolation_type) {
19712 
19713  // Raw resizing.
19714  //
19715  case -1 :
19716  std::memcpy(res.assign(sx,sy,sz,sc,0)._data,_data,sizeof(T)*cimg::min(size(),sx*sy*sz*sc));
19717  break;
19718 
19719  // No interpolation.
19720  //
19721  case 0 : {
19722  const int
19723  xc = (int)(centering_x*((int)sx - width())),
19724  yc = (int)(centering_y*((int)sy - height())),
19725  zc = (int)(centering_z*((int)sz - depth())),
19726  cc = (int)(centering_c*((int)sc - spectrum()));
19727 
19728  switch (boundary_conditions) {
19729  case 2 : { // Cyclic borders.
19730  res.assign(sx,sy,sz,sc);
19731  const int
19732  x0 = ((int)xc%width()) - width(),
19733  y0 = ((int)yc%height()) - height(),
19734  z0 = ((int)zc%depth()) - depth(),
19735  c0 = ((int)cc%spectrum()) - spectrum();
19736  for (int c = c0; c<(int)sc; c+=spectrum())
19737  for (int z = z0; z<(int)sz; z+=depth())
19738  for (int y = y0; y<(int)sy; y+=height())
19739  for (int x = x0; x<(int)sx; x+=width())
19740  res.draw_image(x,y,z,c,*this);
19741  } break;
19742  case 1 : { // Neumann borders.
19743  res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
19744  CImg<T> sprite;
19745  if (xc>0) { // X-backward
19746  res.get_crop(xc,yc,zc,cc,xc,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19747  for (int x = xc-1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
19748  }
19749  if (xc+width()<(int)sx) { // X-forward
19750  res.get_crop(xc+width()-1,yc,zc,cc,xc+width()-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19751  for (int x = xc+width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
19752  }
19753  if (yc>0) { // Y-backward
19754  res.get_crop(0,yc,zc,cc,sx-1,yc,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19755  for (int y = yc-1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
19756  }
19757  if (yc+height()<(int)sy) { // Y-forward
19758  res.get_crop(0,yc+height()-1,zc,cc,sx-1,yc+height()-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19759  for (int y = yc+height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
19760  }
19761  if (zc>0) { // Z-backward
19762  res.get_crop(0,0,zc,cc,sx-1,sy-1,zc,cc+spectrum()-1).move_to(sprite);
19763  for (int z = zc-1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
19764  }
19765  if (zc+depth()<(int)sz) { // Z-forward
19766  res.get_crop(0,0,zc+depth()-1,cc,sx-1,sy-1,zc+depth()-1,cc+spectrum()-1).move_to(sprite);
19767  for (int z = zc+depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
19768  }
19769  if (cc>0) { // C-backward
19770  res.get_crop(0,0,0,cc,sx-1,sy-1,sz-1,cc).move_to(sprite);
19771  for (int c = cc-1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
19772  }
19773  if (cc+spectrum()<(int)sc) { // C-forward
19774  res.get_crop(0,0,0,cc+spectrum()-1,sx-1,sy-1,sz-1,cc+spectrum()-1).move_to(sprite);
19775  for (int c = cc+spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
19776  }
19777  } break;
19778  default : // Dirichlet borders.
19779  res.assign(sx,sy,sz,sc,0).draw_image(xc,yc,zc,cc,*this);
19780  }
19781  break;
19782  } break;
19783 
19784  // Nearest neighbor interpolation.
19785  //
19786  case 1 : {
19787  res.assign(sx,sy,sz,sc);
19788  CImg<ulongT> off_x(sx), off_y(sy+1), off_z(sz+1), off_c(sc+1);
19789  unsigned long *poff_x, *poff_y, *poff_z, *poff_c, curr, old;
19790  const unsigned long
19791  wh = (unsigned long)_width*_height,
19792  whd = (unsigned long)_width*_height*_depth,
19793  sxy = (unsigned long)sx*sy,
19794  sxyz = (unsigned long)sx*sy*sz;
19795  if (sx==_width) off_x.fill(1);
19796  else {
19797  poff_x = off_x._data; curr = 0;
19798  cimg_forX(res,x) { old = curr; curr = ((x+1LU)*_width/sx); *(poff_x++) = curr - old; }
19799  }
19800  if (sy==_height) off_y.fill(_width);
19801  else {
19802  poff_y = off_y._data; curr = 0;
19803  cimg_forY(res,y) { old = curr; curr = ((y+1LU)*_height/sy); *(poff_y++) = _width*(curr - old); } *poff_y = 0;
19804  }
19805  if (sz==_depth) off_z.fill(wh);
19806  else {
19807  poff_z = off_z._data; curr = 0;
19808  cimg_forZ(res,z) { old = curr; curr = ((z+1LU)*_depth/sz); *(poff_z++) = wh*(curr - old); } *poff_z = 0;
19809  }
19810  if (sc==_spectrum) off_c.fill(whd);
19811  else {
19812  poff_c = off_c._data; curr = 0;
19813  cimg_forC(res,c) { old = curr; curr = ((c+1LU)*_spectrum/sc); *(poff_c++) = whd*(curr - old); } *poff_c = 0;
19814  }
19815 
19816  T *ptrd = res._data;
19817  const T* ptrv = _data;
19818  poff_c = off_c._data;
19819  for (unsigned int c = 0; c<sc; ) {
19820  const T *ptrz = ptrv;
19821  poff_z = off_z._data;
19822  for (unsigned int z = 0; z<sz; ) {
19823  const T *ptry = ptrz;
19824  poff_y = off_y._data;
19825  for (unsigned int y = 0; y<sy; ) {
19826  const T *ptrx = ptry;
19827  poff_x = off_x._data;
19828  cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
19829  ++y;
19830  unsigned long dy = *(poff_y++);
19831  for (;!dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
19832  ptry+=dy;
19833  }
19834  ++z;
19835  unsigned long dz = *(poff_z++);
19836  for (;!dz && z<dz; std::memcpy(ptrd,ptrd-sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
19837  ptrz+=dz;
19838  }
19839  ++c;
19840  unsigned long dc = *(poff_c++);
19841  for (;!dc && c<dc; std::memcpy(ptrd,ptrd-sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
19842  ptrv+=dc;
19843  }
19844  } break;
19845 
19846  // Moving average.
19847  //
19848  case 2 : {
19849  bool instance_first = true;
19850  if (sx!=_width) {
19851  CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
19852  for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
19853  const unsigned int d = cimg::min(b,c);
19854  a-=d; b-=d; c-=d;
19855  cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
19856  if (!b) { cimg_forYZC(tmp,y,z,v) tmp(t,y,z,v)/=_width; ++t; b = _width; }
19857  if (!c) { ++s; c = sx; }
19858  }
19859  tmp.move_to(res);
19860  instance_first = false;
19861  }
19862  if (sy!=_height) {
19863  CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
19864  for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
19865  const unsigned int d = cimg::min(b,c);
19866  a-=d; b-=d; c-=d;
19867  if (instance_first) cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
19868  else cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
19869  if (!b) { cimg_forXZC(tmp,x,z,v) tmp(x,t,z,v)/=_height; ++t; b = _height; }
19870  if (!c) { ++s; c = sy; }
19871  }
19872  tmp.move_to(res);
19873  instance_first = false;
19874  }
19875  if (sz!=_depth) {
19876  CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
19877  for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
19878  const unsigned int d = cimg::min(b,c);
19879  a-=d; b-=d; c-=d;
19880  if (instance_first) cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
19881  else cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
19882  if (!b) { cimg_forXYC(tmp,x,y,v) tmp(x,y,t,v)/=_depth; ++t; b = _depth; }
19883  if (!c) { ++s; c = sz; }
19884  }
19885  tmp.move_to(res);
19886  instance_first = false;
19887  }
19888  if (sc!=_spectrum) {
19889  CImg<Tfloat> tmp(sx,sy,sz,sc,0);
19890  for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
19891  const unsigned int d = cimg::min(b,c);
19892  a-=d; b-=d; c-=d;
19893  if (instance_first) cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
19894  else cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
19895  if (!b) { cimg_forXYZ(tmp,x,y,z) tmp(x,y,z,t)/=_spectrum; ++t; b = _spectrum; }
19896  if (!c) { ++s; c = sc; }
19897  }
19898  tmp.move_to(res);
19899  instance_first = false;
19900  }
19901  } break;
19902 
19903  // Linear interpolation.
19904  //
19905  case 3 : {
19906  CImg<uintT> off(cimg::max(sx,sy,sz,sc));
19907  CImg<floatT> foff(off._width);
19908  unsigned int *poff;
19909  float *pfoff, old, curr;
19910  CImg<T> resx, resy, resz, resc;
19911  T *ptrd;
19912 
19913  if (sx!=_width) {
19914  if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
19915  else {
19916  const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
19917  resx.assign(sx,_height,_depth,_spectrum);
19918  curr = old = 0; poff = off._data; pfoff = foff._data;
19919  cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
19920  ptrd = resx._data;
19921  const T *ptrs0 = _data;
19922  cimg_forYZC(resx,y,z,c) {
19923  poff = off._data; pfoff = foff._data;
19924  const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-1);
19925  cimg_forX(resx,x) {
19926  const float alpha = *(pfoff++);
19927  const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+1):val1;
19928  *(ptrd++) = (T)((1-alpha)*val1 + alpha*val2);
19929  ptrs+=*(poff++);
19930  }
19931  ptrs0+=_width;
19932  }
19933  }
19934  } else resx.assign(*this,true);
19935 
19936  if (sy!=_height) {
19937  if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
19938  else {
19939  const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
19940  resy.assign(sx,sy,_depth,_spectrum);
19941  curr = old = 0; poff = off._data; pfoff = foff._data;
19942  cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
19943  cimg_forXZC(resy,x,z,c) {
19944  ptrd = resy.data(x,0,z,c);
19945  const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height-1)*sx;
19946  poff = off._data; pfoff = foff._data;
19947  cimg_forY(resy,y) {
19948  const float alpha = *(pfoff++);
19949  const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sx):val1;
19950  *ptrd = (T)((1-alpha)*val1 + alpha*val2);
19951  ptrd+=sx;
19952  ptrs+=*(poff++);
19953  }
19954  }
19955  }
19956  resx.assign();
19957  } else resy.assign(resx,true);
19958 
19959  if (sz!=_depth) {
19960  if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
19961  else {
19962  const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
19963  const unsigned int sxy = sx*sy;
19964  resz.assign(sx,sy,sz,_spectrum);
19965  curr = old = 0; poff = off._data; pfoff = foff._data;
19966  cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
19967  cimg_forXYC(resz,x,y,c) {
19968  ptrd = resz.data(x,y,0,c);
19969  const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth-1)*sxy;
19970  poff = off._data; pfoff = foff._data;
19971  cimg_forZ(resz,z) {
19972  const float alpha = *(pfoff++);
19973  const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxy):val1;
19974  *ptrd = (T)((1-alpha)*val1 + alpha*val2);
19975  ptrd+=sxy;
19976  ptrs+=*(poff++);
19977  }
19978  }
19979  }
19980  resy.assign();
19981  } else resz.assign(resy,true);
19982 
19983  if (sc!=_spectrum) {
19984  if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
19985  else {
19986  const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
19987  const unsigned int sxyz = sx*sy*sz;
19988  resc.assign(sx,sy,sz,sc);
19989  curr = old = 0; poff = off._data; pfoff = foff._data;
19990  cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
19991  cimg_forXYZ(resc,x,y,z) {
19992  ptrd = resc.data(x,y,z,0);
19993  const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum-1)*sxyz;
19994  poff = off._data; pfoff = foff._data;
19995  cimg_forC(resc,c) {
19996  const float alpha = *(pfoff++);
19997  const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs+sxyz):val1;
19998  *ptrd = (T)((1-alpha)*val1 + alpha*val2);
19999  ptrd+=sxyz;
20000  ptrs+=*(poff++);
20001  }
20002  }
20003  }
20004  resz.assign();
20005  } else resc.assign(resz,true);
20006  return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
20007  } break;
20008 
20009  // Grid interpolation.
20010  //
20011  case 4 : {
20012  CImg<T> resx, resy, resz, resc;
20013  if (sx!=_width) {
20014  if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
20015  else {
20016  resx.assign(sx,_height,_depth,_spectrum,0);
20017  const int dx = sx*2, dy = width()*2;
20018  int err = dy + centering_x*(sx*dy/width() - dy), xs = 0;
20019  cimg_forX(resx,x) if ((err-=dy)<=0) {
20020  cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
20021  ++xs;
20022  err+=dx;
20023  }
20024  }
20025  } else resx.assign(*this,true);
20026 
20027  if (sy!=_height) {
20028  if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
20029  else {
20030  resy.assign(sx,sy,_depth,_spectrum,0);
20031  const int dx = sy*2, dy = height()*2;
20032  int err = dy + centering_y*(sy*dy/height() - dy), ys = 0;
20033  cimg_forY(resy,y) if ((err-=dy)<=0) {
20034  cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
20035  ++ys;
20036  err+=dx;
20037  }
20038  }
20039  resx.assign();
20040  } else resy.assign(resx,true);
20041 
20042  if (sz!=_depth) {
20043  if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
20044  else {
20045  resz.assign(sx,sy,sz,_spectrum,0);
20046  const int dx = sz*2, dy = depth()*2;
20047  int err = dy + centering_z*(sz*dy/depth() - dy), zs = 0;
20048  cimg_forZ(resz,z) if ((err-=dy)<=0) {
20049  cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
20050  ++zs;
20051  err+=dx;
20052  }
20053  }
20054  resy.assign();
20055  } else resz.assign(resy,true);
20056 
20057  if (sc!=_spectrum) {
20058  if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
20059  else {
20060  resc.assign(sx,sy,sz,sc,0);
20061  const int dx = sc*2, dy = spectrum()*2;
20062  int err = dy + centering_c*(sc*dy/spectrum() - dy), cs = 0;
20063  cimg_forC(resc,c) if ((err-=dy)<=0) {
20064  cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
20065  ++cs;
20066  err+=dx;
20067  }
20068  }
20069  resz.assign();
20070  } else resc.assign(resz,true);
20071 
20072  return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
20073  } break;
20074 
20075  // Bicubic interpolation.
20076  //
20077  case 5 : {
20078  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
20079  CImg<uintT> off(cimg::max(sx,sy,sz,sc));
20080  CImg<floatT> foff(off._width);
20081  unsigned int *poff;
20082  float *pfoff, old, curr;
20083  CImg<T> resx, resy, resz, resc;
20084  T *ptrd;
20085 
20086  if (sx!=_width) {
20087  if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
20088  else {
20089  const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
20090  resx.assign(sx,_height,_depth,_spectrum);
20091  curr = old = 0; poff = off._data; pfoff = foff._data;
20092  cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
20093  ptrd = resx._data;
20094  const T *ptrs0 = _data;
20095  cimg_forYZC(resx,y,z,c) {
20096  poff = off._data; pfoff = foff._data;
20097  const T *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_width-2);
20098  cimg_forX(resx,x) {
20099  const float t = *(pfoff++);
20100  const Tfloat
20101  val1 = (Tfloat)*ptrs,
20102  val0 = ptrs>ptrs0?(Tfloat)*(ptrs-1):val1,
20103  val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val1,
20104  val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val2,
20105  val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
20106  *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20107  ptrs+=*(poff++);
20108  }
20109  ptrs0+=_width;
20110  }
20111  }
20112  } else resx.assign(*this,true);
20113 
20114  if (sy!=_height) {
20115  if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
20116  else {
20117  const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
20118  resy.assign(sx,sy,_depth,_spectrum);
20119  curr = old = 0; poff = off._data; pfoff = foff._data;
20120  cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
20121  cimg_forXZC(resy,x,z,c) {
20122  ptrd = resy.data(x,0,z,c);
20123  const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_height-2)*sx;
20124  poff = off._data; pfoff = foff._data;
20125  cimg_forY(resy,y) {
20126  const float t = *(pfoff++);
20127  const Tfloat
20128  val1 = (Tfloat)*ptrs,
20129  val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sx):val1,
20130  val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val1,
20131  val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val2,
20132  val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
20133  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20134  ptrd+=sx;
20135  ptrs+=*(poff++);
20136  }
20137  }
20138  }
20139  resx.assign();
20140  } else resy.assign(resx,true);
20141 
20142  if (sz!=_depth) {
20143  if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
20144  else {
20145  const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
20146  const unsigned int sxy = sx*sy;
20147  resz.assign(sx,sy,sz,_spectrum);
20148  curr = old = 0; poff = off._data; pfoff = foff._data;
20149  cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
20150  cimg_forXYC(resz,x,y,c) {
20151  ptrd = resz.data(x,y,0,c);
20152  const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs0 + (_depth-2)*sxy;
20153  poff = off._data; pfoff = foff._data;
20154  cimg_forZ(resz,z) {
20155  const float t = *(pfoff++);
20156  const Tfloat
20157  val1 = (Tfloat)*ptrs,
20158  val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxy):val1,
20159  val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val1,
20160  val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val2,
20161  val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
20162  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20163  ptrd+=sxy;
20164  ptrs+=*(poff++);
20165  }
20166  }
20167  }
20168  resy.assign();
20169  } else resz.assign(resy,true);
20170 
20171  if (sc!=_spectrum) {
20172  if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
20173  else {
20174  const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
20175  const unsigned int sxyz = sx*sy*sz;
20176  resc.assign(sx,sy,sz,sc);
20177  curr = old = 0; poff = off._data; pfoff = foff._data;
20178  cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
20179  cimg_forXYZ(resc,x,y,z) {
20180  ptrd = resc.data(x,y,z,0);
20181  const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
20182  poff = off._data; pfoff = foff._data;
20183  cimg_forC(resc,c) {
20184  const float t = *(pfoff++);
20185  const Tfloat
20186  val1 = (Tfloat)*ptrs,
20187  val0 = ptrs>ptrs0?(Tfloat)*(ptrs-sxyz):val1,
20188  val2 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val1,
20189  val3 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val2,
20190  val = val1 + 0.5f*(t*(-val0+val2) + t*t*(2*val0-5*val1+4*val2-val3) + t*t*t*(-val0+3*val1-3*val2+val3));
20191  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20192  ptrd+=sxyz;
20193  ptrs+=*(poff++);
20194  }
20195  }
20196  }
20197  resz.assign();
20198  } else resc.assign(resz,true);
20199 
20200  return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
20201  } break;
20202 
20203  // Lanczos interpolation.
20204  //
20205  case 6 : {
20206  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
20207  CImg<uintT> off(cimg::max(sx,sy,sz,sc));
20208  CImg<floatT> foff(off._width);
20209  unsigned int *poff;
20210  float *pfoff, old, curr;
20211  CImg<T> resx, resy, resz, resc;
20212  T *ptrd;
20213 
20214  if (sx!=_width) {
20215  if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
20216  else {
20217  const float fx = (!boundary_conditions && sx>_width)?(sx>1?(_width-1.0f)/(sx-1):0):(float)_width/sx;
20218  resx.assign(sx,_height,_depth,_spectrum);
20219  curr = old = 0; poff = off._data; pfoff = foff._data;
20220  cimg_forX(resx,x) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fx; *(poff++) = (unsigned int)curr - (unsigned int)old; }
20221  ptrd = resx._data;
20222  const T *ptrs0 = _data;
20223  cimg_forYZC(resx,y,z,c) {
20224  poff = off._data; pfoff = foff._data;
20225  const T *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1, *const ptrsmax = ptrs0 + (_width-2);
20226  cimg_forX(resx,x) {
20227  const float
20228  t = *(pfoff++),
20229  w0 = _cimg_lanczos(t+2),
20230  w1 = _cimg_lanczos(t+1),
20231  w2 = _cimg_lanczos(t),
20232  w3 = _cimg_lanczos(t-1),
20233  w4 = _cimg_lanczos(t-2);
20234  const Tfloat
20235  val2 = (Tfloat)*ptrs,
20236  val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-1):val2,
20237  val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2):val1,
20238  val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+1):val2,
20239  val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2):val3,
20240  val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
20241  *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
20242  ptrs+=*(poff++);
20243  }
20244  ptrs0+=_width;
20245  }
20246  }
20247  } else resx.assign(*this,true);
20248 
20249  if (sy!=_height) {
20250  if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
20251  else {
20252  const float fy = (!boundary_conditions && sy>_height)?(sy>1?(_height-1.0f)/(sy-1):0):(float)_height/sy;
20253  resy.assign(sx,sy,_depth,_spectrum);
20254  curr = old = 0; poff = off._data; pfoff = foff._data;
20255  cimg_forY(resy,y) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fy; *(poff++) = sx*((unsigned int)curr-(unsigned int)old); }
20256  cimg_forXZC(resy,x,z,c) {
20257  ptrd = resy.data(x,0,z,c);
20258  const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx, *const ptrsmax = ptrs0 + (_height-2)*sx;
20259  poff = off._data; pfoff = foff._data;
20260  cimg_forY(resy,y) {
20261  const float
20262  t = *(pfoff++),
20263  w0 = _cimg_lanczos(t+2),
20264  w1 = _cimg_lanczos(t+1),
20265  w2 = _cimg_lanczos(t),
20266  w3 = _cimg_lanczos(t-1),
20267  w4 = _cimg_lanczos(t-2);
20268  const Tfloat
20269  val2 = (Tfloat)*ptrs,
20270  val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sx):val2,
20271  val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sx):val1,
20272  val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sx):val2,
20273  val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sx):val3,
20274  val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
20275  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20276  ptrd+=sx;
20277  ptrs+=*(poff++);
20278  }
20279  }
20280  }
20281  resx.assign();
20282  } else resy.assign(resx,true);
20283 
20284  if (sz!=_depth) {
20285  if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
20286  else {
20287  const float fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth-1.0f)/(sz-1):0):(float)_depth/sz;
20288  const unsigned int sxy = sx*sy;
20289  resz.assign(sx,sy,sz,_spectrum);
20290  curr = old = 0; poff = off._data; pfoff = foff._data;
20291  cimg_forZ(resz,z) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fz; *(poff++) = sxy*((unsigned int)curr - (unsigned int)old); }
20292  cimg_forXYC(resz,x,y,c) {
20293  ptrd = resz.data(x,y,0,c);
20294  const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy, *const ptrsmax = ptrs0 + (_depth-2)*sxy;
20295  poff = off._data; pfoff = foff._data;
20296  cimg_forZ(resz,z) {
20297  const float
20298  t = *(pfoff++),
20299  w0 = _cimg_lanczos(t+2),
20300  w1 = _cimg_lanczos(t+1),
20301  w2 = _cimg_lanczos(t),
20302  w3 = _cimg_lanczos(t-1),
20303  w4 = _cimg_lanczos(t-2);
20304  const Tfloat
20305  val2 = (Tfloat)*ptrs,
20306  val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxy):val2,
20307  val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxy):val1,
20308  val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxy):val2,
20309  val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxy):val3,
20310  val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
20311  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20312  ptrd+=sxy;
20313  ptrs+=*(poff++);
20314  }
20315  }
20316  }
20317  resy.assign();
20318  } else resz.assign(resy,true);
20319 
20320  if (sc!=_spectrum) {
20321  if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
20322  else {
20323  const float fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum-1.0f)/(sc-1):0):(float)_spectrum/sc;
20324  const unsigned int sxyz = sx*sy*sz;
20325  resc.assign(sx,sy,sz,sc);
20326  curr = old = 0; poff = off._data; pfoff = foff._data;
20327  cimg_forC(resc,c) { *(pfoff++) = curr - (unsigned int)curr; old = curr; curr+=fc; *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old); }
20328  cimg_forXYZ(resc,x,y,z) {
20329  ptrd = resc.data(x,y,z,0);
20330  const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz, *const ptrsmax = ptrs + (_spectrum-2)*sxyz;
20331  poff = off._data; pfoff = foff._data;
20332  cimg_forC(resc,c) {
20333  const float
20334  t = *(pfoff++),
20335  w0 = _cimg_lanczos(t+2),
20336  w1 = _cimg_lanczos(t+1),
20337  w2 = _cimg_lanczos(t),
20338  w3 = _cimg_lanczos(t-1),
20339  w4 = _cimg_lanczos(t-2);
20340  const Tfloat
20341  val2 = (Tfloat)*ptrs,
20342  val1 = ptrs>=ptrsmin?(Tfloat)*(ptrs-sxyz):val2,
20343  val0 = ptrs>ptrsmin?(Tfloat)*(ptrs-2*sxyz):val1,
20344  val3 = ptrs<=ptrsmax?(Tfloat)*(ptrs+sxyz):val2,
20345  val4 = ptrs<ptrsmax?(Tfloat)*(ptrs+2*sxyz):val3,
20346  val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
20347  *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
20348  ptrd+=sxyz;
20349  ptrs+=*(poff++);
20350  }
20351  }
20352  }
20353  resz.assign();
20354  } else resc.assign(resz,true);
20355 
20356  return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
20357  } break;
20358 
20359  // Unknow interpolation.
20360  //
20361  default :
20362  throw CImgArgumentException(_cimg_instance
20363  "resize(): Invalid specified interpolation %d "
20364  "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | 5=bicubic | 6=lanczos }).",
20365  cimg_instance,
20366  interpolation_type);
20367  }
20368  return res;
20369  }
20370 
20372 
20381  template<typename t>
20382  CImg<T>& resize(const CImg<t>& src,
20383  const int interpolation_type=1, const unsigned int boundary_conditions=0,
20384  const float centering_x = 0, const float centering_y = 0,
20385  const float centering_z = 0, const float centering_c = 0) {
20386  return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
20387  centering_x,centering_y,centering_z,centering_c);
20388  }
20389 
20391  template<typename t>
20393  const int interpolation_type=1, const unsigned int boundary_conditions=0,
20394  const float centering_x = 0, const float centering_y = 0,
20395  const float centering_z = 0, const float centering_c = 0) const {
20396  return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
20397  centering_x,centering_y,centering_z,centering_c);
20398  }
20399 
20401 
20411  const int interpolation_type=1, const unsigned int boundary_conditions=0,
20412  const float centering_x = 0, const float centering_y = 0,
20413  const float centering_z = 0, const float centering_c = 0) {
20414  return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
20415  centering_x,centering_y,centering_z,centering_c);
20416  }
20417 
20420  const int interpolation_type=1, const unsigned int boundary_conditions=0,
20421  const float centering_x = 0, const float centering_y = 0,
20422  const float centering_z = 0, const float centering_c = 0) const {
20423  return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
20424  centering_x,centering_y,centering_z,centering_c);
20425  }
20426 
20429  return get_resize_halfXY().move_to(*this);
20430  }
20431 
20434  if (is_empty()) return *this;
20435  const Tfloat mask[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
20436  0.1231940459f, 0.1935127547f, 0.1231940459f,
20437  0.07842776544f, 0.1231940459f, 0.07842776544f };
20438  T I[9] = { 0 };
20439  CImg<T> res(_width/2,_height/2,_depth,_spectrum);
20440  T *ptrd = res._data;
20441  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
20442  if (x%2 && y%2) *(ptrd++) = (T)
20443  (I[0]*mask[0] + I[1]*mask[1] + I[2]*mask[2] +
20444  I[3]*mask[3] + I[4]*mask[4] + I[5]*mask[5] +
20445  I[6]*mask[6] + I[7]*mask[7] + I[8]*mask[8]);
20446  return res;
20447  }
20448 
20450 
20454  return get_resize_doubleXY().move_to(*this);
20455  }
20456 
20459 #define _cimg_gs2x_for3(bound,i) \
20460  for (int i = 0, _p1##i = 0, \
20461  _n1##i = 1>=(bound)?(int)(bound)-1:1; \
20462  _n1##i<(int)(bound) || i==--_n1##i; \
20463  _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
20464 
20465 #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
20466  _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
20467  _p1##x = 0, \
20468  _n1##x = (int)( \
20469  (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
20470  (I[3] = I[4] = (T)(img)(0,y,z,c)), \
20471  (I[7] = (T)(img)(0,_n1##y,z,c)), \
20472  1>=(img)._width?(img).width()-1:1); \
20473  (_n1##x<(img).width() && ( \
20474  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
20475  (I[5] = (T)(img)(_n1##x,y,z,c)), \
20476  (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
20477  x==--_n1##x; \
20478  I[1] = I[2], \
20479  I[3] = I[4], I[4] = I[5], \
20480  I[7] = I[8], \
20481  _p1##x = x++, ++_n1##x)
20482 
20483  if (is_empty()) return *this;
20484  CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
20485  CImg_3x3(I,T);
20486  cimg_forZC(*this,z,c) {
20487  T
20488  *ptrd1 = res.data(0,0,z,c),
20489  *ptrd2 = ptrd1 + res._width;
20490  _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
20491  if (Icp!=Icn && Ipc!=Inc) {
20492  *(ptrd1++) = Ipc==Icp?Ipc:Icc;
20493  *(ptrd1++) = Icp==Inc?Inc:Icc;
20494  *(ptrd2++) = Ipc==Icn?Ipc:Icc;
20495  *(ptrd2++) = Icn==Inc?Inc:Icc;
20496  } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
20497  }
20498  }
20499  return res;
20500  }
20501 
20503 
20507  return get_resize_tripleXY().move_to(*this);
20508  }
20509 
20512 #define _cimg_gs3x_for3(bound,i) \
20513  for (int i = 0, _p1##i = 0, \
20514  _n1##i = 1>=(bound)?(int)(bound)-1:1; \
20515  _n1##i<(int)(bound) || i==--_n1##i; \
20516  _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
20517 
20518 #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
20519  _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
20520  _p1##x = 0, \
20521  _n1##x = (int)( \
20522  (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
20523  (I[3] = I[4] = (T)(img)(0,y,z,c)), \
20524  (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
20525  1>=(img)._width?(img).width()-1:1); \
20526  (_n1##x<(img).width() && ( \
20527  (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
20528  (I[5] = (T)(img)(_n1##x,y,z,c)), \
20529  (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
20530  x==--_n1##x; \
20531  I[0] = I[1], I[1] = I[2], \
20532  I[3] = I[4], I[4] = I[5], \
20533  I[6] = I[7], I[7] = I[8], \
20534  _p1##x = x++, ++_n1##x)
20535 
20536  if (is_empty()) return *this;
20537  CImg<T> res(3*_width,3*_height,_depth,_spectrum);
20538  CImg_3x3(I,T);
20539  cimg_forZC(*this,z,c) {
20540  T
20541  *ptrd1 = res.data(0,0,z,c),
20542  *ptrd2 = ptrd1 + res._width,
20543  *ptrd3 = ptrd2 + res._width;
20544  _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
20545  if (Icp != Icn && Ipc != Inc) {
20546  *(ptrd1++) = Ipc==Icp?Ipc:Icc;
20547  *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
20548  *(ptrd1++) = Icp==Inc?Inc:Icc;
20549  *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
20550  *(ptrd2++) = Icc;
20551  *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
20552  *(ptrd3++) = Ipc==Icn?Ipc:Icc;
20553  *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
20554  *(ptrd3++) = Icn==Inc?Inc:Icc;
20555  } else {
20556  *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
20557  *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
20558  *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
20559  }
20560  }
20561  }
20562  return res;
20563  }
20564 
20566 
20569  CImg<T>& mirror(const char axis) {
20570  if (is_empty()) return *this;
20571  T *pf, *pb, *buf = 0;
20572  switch (cimg::uncase(axis)) {
20573  case 'x' : {
20574  pf = _data; pb = data(_width-1);
20575  const unsigned int width2 = _width/2;
20576  for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
20577  for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
20578  pf+=_width - width2;
20579  pb+=_width + width2;
20580  }
20581  } break;
20582  case 'y' : {
20583  buf = new T[_width];
20584  pf = _data; pb = data(0,_height-1);
20585  const unsigned int height2 = _height/2;
20586  for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
20587  for (unsigned int y = 0; y<height2; ++y) {
20588  std::memcpy(buf,pf,_width*sizeof(T));
20589  std::memcpy(pf,pb,_width*sizeof(T));
20590  std::memcpy(pb,buf,_width*sizeof(T));
20591  pf+=_width;
20592  pb-=_width;
20593  }
20594  pf+=(unsigned long)_width*(_height - height2);
20595  pb+=(unsigned long)_width*(_height + height2);
20596  }
20597  } break;
20598  case 'z' : {
20599  buf = new T[(unsigned long)_width*_height];
20600  pf = _data; pb = data(0,0,_depth-1);
20601  const unsigned int depth2 = _depth/2;
20602  cimg_forC(*this,c) {
20603  for (unsigned int z = 0; z<depth2; ++z) {
20604  std::memcpy(buf,pf,_width*_height*sizeof(T));
20605  std::memcpy(pf,pb,_width*_height*sizeof(T));
20606  std::memcpy(pb,buf,_width*_height*sizeof(T));
20607  pf+=(unsigned long)_width*_height;
20608  pb-=(unsigned long)_width*_height;
20609  }
20610  pf+=(unsigned long)_width*_height*(_depth - depth2);
20611  pb+=(unsigned long)_width*_height*(_depth + depth2);
20612  }
20613  } break;
20614  case 'c' : {
20615  buf = new T[(unsigned long)_width*_height*_depth];
20616  pf = _data; pb = data(0,0,0,_spectrum-1);
20617  const unsigned int _spectrum2 = _spectrum/2;
20618  for (unsigned int v = 0; v<_spectrum2; ++v) {
20619  std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
20620  std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
20621  std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
20622  pf+=(unsigned long)_width*_height*_depth;
20623  pb-=(unsigned long)_width*_height*_depth;
20624  }
20625  } break;
20626  default :
20627  throw CImgArgumentException(_cimg_instance
20628  "mirror(): Invalid specified axis '%c'.",
20629  cimg_instance,
20630  axis);
20631  }
20632  delete[] buf;
20633  return *this;
20634  }
20635 
20637  CImg<T> get_mirror(const char axis) const {
20638  return (+*this).mirror(axis);
20639  }
20640 
20642 
20646  CImg<T>& mirror(const char *const axes) {
20647  for (const char *s = axes; *s; s++) mirror(*s);
20648  return *this;
20649  }
20650 
20652  CImg<T> get_mirror(const char *const axes) const {
20653  return (+*this).mirror(axes);
20654  }
20655 
20657 
20669  CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
20670  const int boundary_conditions=0) {
20671  if (is_empty()) return *this;
20672  if (delta_x) // Shift along X-axis
20673  switch (boundary_conditions) {
20674  case 0 :
20675  if (cimg::abs(delta_x)>=width()) return fill(0);
20676  if (delta_x<0) cimg_forYZC(*this,y,z,c) {
20677  std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width+delta_x)*sizeof(T));
20678  std::memset(data(_width+delta_x,y,z,c),0,-delta_x*sizeof(T));
20679  } else cimg_forYZC(*this,y,z,c) {
20680  std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
20681  std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
20682  }
20683  break;
20684  case 1 :
20685  if (delta_x<0) {
20686  const int ndelta_x = (-delta_x>=width())?width()-1:-delta_x;
20687  if (!ndelta_x) return *this;
20688  cimg_forYZC(*this,y,z,c) {
20689  std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
20690  T *ptrd = data(_width-1,y,z,c);
20691  const T val = *ptrd;
20692  for (int l = 0; l<ndelta_x-1; ++l) *(--ptrd) = val;
20693  }
20694  } else {
20695  const int ndelta_x = (delta_x>=width())?width()-1:delta_x;
20696  if (!ndelta_x) return *this;
20697  cimg_forYZC(*this,y,z,c) {
20698  std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
20699  T *ptrd = data(0,y,z,c);
20700  const T val = *ptrd;
20701  for (int l = 0; l<ndelta_x-1; ++l) *(++ptrd) = val;
20702  }
20703  }
20704  break;
20705  default : {
20706  const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
20707  if (!ndelta_x) return *this;
20708  T *const buf = new T[cimg::abs(ndelta_x)];
20709  if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
20710  std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
20711  std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
20712  std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
20713  } else cimg_forYZC(*this,y,z,c) {
20714  std::memcpy(buf,data(_width+ndelta_x,y,z,c),-ndelta_x*sizeof(T));
20715  std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width+ndelta_x)*sizeof(T));
20716  std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
20717  }
20718  delete[] buf;
20719  }
20720  }
20721 
20722  if (delta_y) // Shift along Y-axis
20723  switch (boundary_conditions) {
20724  case 0 :
20725  if (cimg::abs(delta_y)>=height()) return fill(0);
20726  if (delta_y<0) cimg_forZC(*this,z,c) {
20727  std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height+delta_y)*sizeof(T));
20728  std::memset(data(0,_height+delta_y,z,c),0,-delta_y*_width*sizeof(T));
20729  } else cimg_forZC(*this,z,c) {
20730  std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
20731  std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
20732  }
20733  break;
20734  case 1 :
20735  if (delta_y<0) {
20736  const int ndelta_y = (-delta_y>=height())?height()-1:-delta_y;
20737  if (!ndelta_y) return *this;
20738  cimg_forZC(*this,z,c) {
20739  std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
20740  T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height-1,z,c);
20741  for (int l = 0; l<ndelta_y-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
20742  }
20743  } else {
20744  const int ndelta_y = (delta_y>=height())?height()-1:delta_y;
20745  if (!ndelta_y) return *this;
20746  cimg_forZC(*this,z,c) {
20747  std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
20748  T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
20749  for (int l = 0; l<ndelta_y-1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
20750  }
20751  }
20752  break;
20753  default : {
20754  const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
20755  if (!ndelta_y) return *this;
20756  T *const buf = new T[(unsigned long)_width*cimg::abs(ndelta_y)];
20757  if (ndelta_y>0) cimg_forZC(*this,z,c) {
20758  std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
20759  std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
20760  std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
20761  } else cimg_forZC(*this,z,c) {
20762  std::memcpy(buf,data(0,_height+ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
20763  std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height+ndelta_y)*sizeof(T));
20764  std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
20765  }
20766  delete[] buf;
20767  }
20768  }
20769 
20770  if (delta_z) // Shift along Z-axis
20771  switch (boundary_conditions) {
20772  case 0 :
20773  if (cimg::abs(delta_z)>=depth()) return fill(0);
20774  if (delta_z<0) cimg_forC(*this,c) {
20775  std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth+delta_z)*sizeof(T));
20776  std::memset(data(0,0,_depth+delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
20777  } else cimg_forC(*this,c) {
20778  std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
20779  std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
20780  }
20781  break;
20782  case 1 :
20783  if (delta_z<0) {
20784  const int ndelta_z = (-delta_z>=depth())?depth()-1:-delta_z;
20785  if (!ndelta_z) return *this;
20786  cimg_forC(*this,c) {
20787  std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
20788  T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth-1,c);
20789  for (int l = 0; l<ndelta_z-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(unsigned long)_width*_height; }
20790  }
20791  } else {
20792  const int ndelta_z = (delta_z>=depth())?depth()-1:delta_z;
20793  if (!ndelta_z) return *this;
20794  cimg_forC(*this,c) {
20795  std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
20796  T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
20797  for (int l = 0; l<ndelta_z-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(unsigned long)_width*_height; }
20798  }
20799  }
20800  break;
20801  default : {
20802  const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
20803  if (!ndelta_z) return *this;
20804  T *const buf = new T[(unsigned long)_width*_height*cimg::abs(ndelta_z)];
20805  if (ndelta_z>0) cimg_forC(*this,c) {
20806  std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
20807  std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
20808  std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
20809  } else cimg_forC(*this,c) {
20810  std::memcpy(buf,data(0,0,_depth+ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
20811  std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth+ndelta_z)*sizeof(T));
20812  std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
20813  }
20814  delete[] buf;
20815  }
20816  }
20817 
20818  if (delta_c) // Shift along C-axis
20819  switch (boundary_conditions) {
20820  case 0 :
20821  if (cimg::abs(delta_c)>=spectrum()) return fill(0);
20822  if (delta_c<0) {
20823  std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum+delta_c)*sizeof(T));
20824  std::memset(data(0,0,0,_spectrum+delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
20825  } else {
20826  std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
20827  std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
20828  }
20829  break;
20830  case 1 :
20831  if (delta_c<0) {
20832  const int ndelta_c = (-delta_c>=spectrum())?spectrum()-1:-delta_c;
20833  if (!ndelta_c) return *this;
20834  std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
20835  T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum-1);
20836  for (int l = 0; l<ndelta_c-1; ++l) { std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(unsigned long)_width*_height*_depth; }
20837  } else {
20838  const int ndelta_c = (delta_c>=spectrum())?spectrum()-1:delta_c;
20839  if (!ndelta_c) return *this;
20840  std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
20841  T *ptrd = data(0,0,0,1);
20842  for (int l = 0; l<ndelta_c-1; ++l) { std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(unsigned long)_width*_height*_depth; }
20843  }
20844  break;
20845  default : {
20846  const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
20847  if (!ndelta_c) return *this;
20848  T *const buf = new T[(unsigned long)_width*_height*_depth*cimg::abs(ndelta_c)];
20849  if (ndelta_c>0) {
20850  std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
20851  std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
20852  std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
20853  } else {
20854  std::memcpy(buf,data(0,0,0,_spectrum+ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
20855  std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum+ndelta_c)*sizeof(T));
20856  std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
20857  }
20858  delete[] buf;
20859  }
20860  }
20861  return *this;
20862  }
20863 
20865  CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
20866  const int boundary_conditions=0) const {
20867  return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
20868  }
20869 
20871 
20875  CImg<T>& permute_axes(const char *const order) {
20876  return get_permute_axes(order).move_to(*this);
20877  }
20878 
20880  CImg<T> get_permute_axes(const char *const order) const {
20881  const T foo = (T)0;
20882  return _get_permute_axes(order,foo);
20883  }
20884 
20885  template<typename t>
20886  CImg<t> _get_permute_axes(const char *const permut, const t&) const {
20887  if (is_empty() || !permut) return CImg<t>(*this,false);
20888  CImg<t> res;
20889  const T* ptrs = _data;
20890  if (!cimg::strncasecmp(permut,"xyzc",4)) return +*this;
20891  if (!cimg::strncasecmp(permut,"xycz",4)) {
20892  res.assign(_width,_height,_spectrum,_depth);
20893  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20894  cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
20895  }
20896  if (!cimg::strncasecmp(permut,"xzyc",4)) {
20897  res.assign(_width,_depth,_height,_spectrum);
20898  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20899  cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
20900  }
20901  if (!cimg::strncasecmp(permut,"xzcy",4)) {
20902  res.assign(_width,_depth,_spectrum,_height);
20903  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20904  cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
20905  }
20906  if (!cimg::strncasecmp(permut,"xcyz",4)) {
20907  res.assign(_width,_spectrum,_height,_depth);
20908  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20909  cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
20910  }
20911  if (!cimg::strncasecmp(permut,"xczy",4)) {
20912  res.assign(_width,_spectrum,_depth,_height);
20913  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20914  cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
20915  }
20916  if (!cimg::strncasecmp(permut,"yxzc",4)) {
20917  res.assign(_height,_width,_depth,_spectrum);
20918  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20919  cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
20920  }
20921  if (!cimg::strncasecmp(permut,"yxcz",4)) {
20922  res.assign(_height,_width,_spectrum,_depth);
20923  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20924  cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
20925  }
20926  if (!cimg::strncasecmp(permut,"yzxc",4)) {
20927  res.assign(_height,_depth,_width,_spectrum);
20928  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20929  cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
20930  }
20931  if (!cimg::strncasecmp(permut,"yzcx",4)) {
20932  res.assign(_height,_depth,_spectrum,_width);
20933  switch (_width) {
20934  case 1 : {
20935  t *ptr_r = res.data(0,0,0,0);
20936  for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20937  *(ptr_r++) = (t)*(ptrs++);
20938  }
20939  } break;
20940  case 2 : {
20941  t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
20942  for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20943  *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++);
20944  }
20945  } break;
20946  case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
20947  t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2);
20948  for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20949  *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++);
20950  }
20951  } break;
20952  case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
20953  t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
20954  for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
20955  *(ptr_r++) = (t)*(ptrs++); *(ptr_g++) = (t)*(ptrs++); *(ptr_b++) = (t)*(ptrs++); *(ptr_a++) = (t)*(ptrs++);
20956  }
20957  } break;
20958  default : {
20959  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20960  cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
20961  return res;
20962  }
20963  }
20964  }
20965  if (!cimg::strncasecmp(permut,"ycxz",4)) {
20966  res.assign(_height,_spectrum,_width,_depth);
20967  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20968  cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
20969  }
20970  if (!cimg::strncasecmp(permut,"yczx",4)) {
20971  res.assign(_height,_spectrum,_depth,_width);
20972  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20973  cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
20974  }
20975  if (!cimg::strncasecmp(permut,"zxyc",4)) {
20976  res.assign(_depth,_width,_height,_spectrum);
20977  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20978  cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
20979  }
20980  if (!cimg::strncasecmp(permut,"zxcy",4)) {
20981  res.assign(_depth,_width,_spectrum,_height);
20982  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20983  cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
20984  }
20985  if (!cimg::strncasecmp(permut,"zyxc",4)) {
20986  res.assign(_depth,_height,_width,_spectrum);
20987  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20988  cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
20989  }
20990  if (!cimg::strncasecmp(permut,"zycx",4)) {
20991  res.assign(_depth,_height,_spectrum,_width);
20992  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20993  cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
20994  }
20995  if (!cimg::strncasecmp(permut,"zcxy",4)) {
20996  res.assign(_depth,_spectrum,_width,_height);
20997  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
20998  cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
20999  }
21000  if (!cimg::strncasecmp(permut,"zcyx",4)) {
21001  res.assign(_depth,_spectrum,_height,_width);
21002  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21003  cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
21004  }
21005  if (!cimg::strncasecmp(permut,"cxyz",4)) {
21006  res.assign(_spectrum,_width,_height,_depth);
21007  switch (_spectrum) {
21008  case 1 : {
21009  const T *ptr_r = data(0,0,0,0);
21010  t *ptrd = res._data;
21011  for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
21012  } break;
21013  case 2 : {
21014  const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
21015  t *ptrd = res._data;
21016  for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) {
21017  *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++);
21018  }
21019  } break;
21020  case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
21021  const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
21022  t *ptrd = res._data;
21023  for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) {
21024  *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++);
21025  }
21026  } break;
21027  case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
21028  const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
21029  t *ptrd = res._data;
21030  for (unsigned long siz = (unsigned long)_width*_height*_depth; siz; --siz) {
21031  *(ptrd++) = (t)*(ptr_r++); *(ptrd++) = (t)*(ptr_g++); *(ptrd++) = (t)*(ptr_b++); *(ptrd++) = (t)*(ptr_a++);
21032  }
21033  } break;
21034  default : {
21035  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21036  cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
21037  }
21038  }
21039  }
21040  if (!cimg::strncasecmp(permut,"cxzy",4)) {
21041  res.assign(_spectrum,_width,_depth,_height);
21042  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21043  cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
21044  }
21045  if (!cimg::strncasecmp(permut,"cyxz",4)) {
21046  res.assign(_spectrum,_height,_width,_depth);
21047  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21048  cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
21049  }
21050  if (!cimg::strncasecmp(permut,"cyzx",4)) {
21051  res.assign(_spectrum,_height,_depth,_width);
21052  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21053  cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
21054  }
21055  if (!cimg::strncasecmp(permut,"czxy",4)) {
21056  res.assign(_spectrum,_depth,_width,_height);
21057  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21058  cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
21059  }
21060  if (!cimg::strncasecmp(permut,"czyx",4)) {
21061  res.assign(_spectrum,_depth,_height,_width);
21062  const unsigned long wh = (unsigned long)res._width*res._height, whd = wh*res._depth;
21063  cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
21064  }
21065  if (!res)
21066  throw CImgArgumentException(_cimg_instance
21067  "permute_axes(): Invalid specified permutation '%s'.",
21068  cimg_instance,
21069  permut);
21070  return res;
21071  }
21072 
21074 
21077  CImg<T>& unroll(const char axis) {
21078  const unsigned int siz = size();
21079  if (siz) switch (axis) {
21080  case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
21081  case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
21082  case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
21083  default : _spectrum = siz; _width = _height = _depth = 1;
21084  }
21085  return *this;
21086  }
21087 
21089  CImg<T> get_unroll(const char axis) const {
21090  return (+*this).unroll(axis);
21091  }
21092 
21094 
21100  CImg<T>& rotate(const float angle, const unsigned int boundary_conditions=0, const unsigned int interpolation_type=1) {
21101  return get_rotate(angle,boundary_conditions,interpolation_type).move_to(*this);
21102  }
21103 
21105  CImg<T> get_rotate(const float angle, const unsigned int boundary_conditions=0, const unsigned int interpolation_type=1) const {
21106  if (is_empty()) return *this;
21107  CImg<T> res;
21108  const float nangle = cimg::mod(angle,360.0f);
21109  if (boundary_conditions!=1 && cimg::mod(nangle,90.0f)==0) { // optimized version for orthogonal angles
21110  const int wm1 = width() - 1, hm1 = height() - 1;
21111  const int iangle = (int)nangle/90;
21112  switch (iangle) {
21113  case 1 : {
21114  res.assign(_height,_width,_depth,_spectrum);
21115  T *ptrd = res._data;
21116  cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1-x,z,c);
21117  } break;
21118  case 2 : {
21119  res.assign(_width,_height,_depth,_spectrum);
21120  T *ptrd = res._data;
21121  cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-x,hm1-y,z,c);
21122  } break;
21123  case 3 : {
21124  res.assign(_height,_width,_depth,_spectrum);
21125  T *ptrd = res._data;
21126  cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1-y,x,z,c);
21127  } break;
21128  default :
21129  return *this;
21130  }
21131  } else { // generic version
21132  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
21133  const float
21134  rad = (float)(nangle*cimg::PI/180.0),
21135  ca = (float)std::cos(rad),
21136  sa = (float)std::sin(rad),
21137  ux = cimg::abs(_width*ca), uy = cimg::abs(_width*sa),
21138  vx = cimg::abs(_height*sa), vy = cimg::abs(_height*ca),
21139  w2 = 0.5f*_width, h2 = 0.5f*_height,
21140  dw2 = 0.5f*(ux+vx), dh2 = 0.5f*(uy+vy);
21141  res.assign((int)(ux+vx),(int)(uy+vy),_depth,_spectrum);
21142  switch (boundary_conditions) {
21143  case 0 : {
21144  switch (interpolation_type) {
21145  case 2 : {
21146  cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21147  const Tfloat val = cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0);
21148  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21149  }
21150  } break;
21151  case 1 : {
21152  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21153  res(x,y,z,c) = (T)linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c,0);
21154  } break;
21155  default : {
21156  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21157  res(x,y,z,c) = atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c,0);
21158  }
21159  }
21160  } break;
21161  case 1 : {
21162  switch (interpolation_type) {
21163  case 2 : {
21164  cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21165  const Tfloat val = _cubic_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
21166  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21167  }
21168  } break;
21169  case 1 : {
21170  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21171  res(x,y,z,c) = (T)_linear_atXY(w2 + (x-dw2)*ca + (y-dh2)*sa,h2 - (x-dw2)*sa + (y-dh2)*ca,z,c);
21172  } break;
21173  default : {
21174  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21175  res(x,y,z,c) = _atXY((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),(int)(h2 - (x-dw2)*sa + (y-dh2)*ca),z,c);
21176  }
21177  }
21178  } break;
21179  case 2 : {
21180  switch (interpolation_type) {
21181  case 2 : {
21182  cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21183  const Tfloat val = _cubic_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
21184  cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
21185  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21186  }
21187  } break;
21188  case 1 : {
21189  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21190  res(x,y,z,c) = (T)_linear_atXY(cimg::mod(w2 + (x-dw2)*ca + (y-dh2)*sa,(float)width()),
21191  cimg::mod(h2 - (x-dw2)*sa + (y-dh2)*ca,(float)height()),z,c);
21192  } break;
21193  default : {
21194  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21195  res(x,y,z,c) = (*this)(cimg::mod((int)(w2 + (x-dw2)*ca + (y-dh2)*sa),width()),
21196  cimg::mod((int)(h2 - (x-dw2)*sa + (y-dh2)*ca),height()),z,c);
21197  }
21198  }
21199  } break;
21200  default :
21201  throw CImgArgumentException(_cimg_instance
21202  "rotate(): Invalid specified border conditions %d "
21203  "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).",
21204  cimg_instance,
21205  boundary_conditions);
21206  }
21207  }
21208  return res;
21209  }
21210 
21212 
21220  CImg<T>& rotate(const float angle, const float cx, const float cy, const float zoom,
21221  const unsigned int boundary_conditions=3, const unsigned int interpolation_type=1) {
21222  return get_rotate(angle,cx,cy,zoom,boundary_conditions,interpolation_type).move_to(*this);
21223  }
21224 
21226  CImg<T> get_rotate(const float angle, const float cx, const float cy, const float zoom,
21227  const unsigned int boundary_conditions=3, const unsigned int interpolation_type=1) const {
21228  if (interpolation_type>2)
21229  throw CImgArgumentException(_cimg_instance
21230  "rotate(): Invalid specified interpolation type %d "
21231  "(should be { 0=none | 1=linear | 2=bicubic }).",
21232  cimg_instance,
21233  interpolation_type);
21234 
21235  if (is_empty()) return *this;
21236  CImg<T> res(_width,_height,_depth,_spectrum);
21237  const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
21238  const float
21239  rad = (float)((angle*cimg::PI)/180.0),
21240  ca = (float)std::cos(rad)/zoom,
21241  sa = (float)std::sin(rad)/zoom;
21242  switch (boundary_conditions) {
21243  case 0 : {
21244  switch (interpolation_type) {
21245  case 2 : {
21246  cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21247  const Tfloat val = cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0);
21248  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21249  }
21250  } break;
21251  case 1 : {
21252  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21253  res(x,y,z,c) = (T)linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c,0);
21254  } break;
21255  default : {
21256  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21257  res(x,y,z,c) = atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c,0);
21258  }
21259  }
21260  } break;
21261  case 1 : {
21262  switch (interpolation_type) {
21263  case 2 : {
21264  cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21265  const Tfloat val = _cubic_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
21266  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21267  }
21268  } break;
21269  case 1 : {
21270  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21271  res(x,y,z,c) = (T)_linear_atXY(cx + (x-cx)*ca + (y-cy)*sa,cy - (x-cx)*sa + (y-cy)*ca,z,c);
21272  } break;
21273  default : {
21274  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21275  res(x,y,z,c) = _atXY((int)(cx + (x-cx)*ca + (y-cy)*sa),(int)(cy - (x-cx)*sa + (y-cy)*ca),z,c);
21276  }
21277  }
21278  } break;
21279  case 2 : {
21280  switch (interpolation_type) {
21281  case 2 : {
21282  cimg_forXY(res,x,y) cimg_forZC(*this,z,c) {
21283  const Tfloat val = _cubic_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
21284  cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
21285  res(x,y,z,c) = (T)(val<vmin?vmin:val>vmax?vmax:val);
21286  }
21287  } break;
21288  case 1 : {
21289  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21290  res(x,y,z,c) = (T)_linear_atXY(cimg::mod(cx + (x-cx)*ca + (y-cy)*sa,(float)width()),
21291  cimg::mod(cy - (x-cx)*sa + (y-cy)*ca,(float)height()),z,c);
21292  } break;
21293  default : {
21294  cimg_forXY(res,x,y) cimg_forZC(*this,z,c)
21295  res(x,y,z,c) = (*this)(cimg::mod((int)(cx + (x-cx)*ca + (y-cy)*sa),width()),
21296  cimg::mod((int)(cy - (x-cx)*sa + (y-cy)*ca),height()),z,c);
21297  }
21298  }
21299  } break;
21300  default :
21301  throw CImgArgumentException(_cimg_instance
21302  "rotate(): Invalid specified border conditions %d "
21303  "(should be { 0=dirichlet | 1=neumann | 2=cyclic }).",
21304  cimg_instance,
21305  boundary_conditions);
21306  }
21307  return res;
21308  }
21309 
21311 
21317  template<typename t>
21318  CImg<T>& warp(const CImg<t>& warp, const bool is_relative=false,
21319  const bool is_linear_interpolation=true, const unsigned int boundary_conditions=0) {
21320  return get_warp(warp,is_relative,is_linear_interpolation,boundary_conditions).move_to(*this);
21321  }
21322 
21324  template<typename t>
21325  CImg<T> get_warp(const CImg<t>& warp, const bool is_relative=false,
21326  const bool is_linear_interpolation=true, const unsigned int boundary_conditions=0) const {
21327  if (is_empty() || !warp) return *this;
21328  if (is_relative && !is_sameXYZ(warp))
21329  throw CImgArgumentException(_cimg_instance
21330  "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
21331  "have different XYZ dimensions.",
21332  cimg_instance,
21333  warp._width,warp._height,warp._depth,warp._spectrum,warp._data);
21334 
21335  CImg<T> res(warp._width,warp._height,warp._depth,_spectrum);
21336  T *ptrd = res._data;
21337  switch (warp._spectrum) {
21338  case 1 : // 1d warping.
21339  if (is_relative) { // Relative warp coordinates
21340  if (is_linear_interpolation) switch (boundary_conditions) {
21341  case 2 : {
21342  cimg_forC(res,c) {
21343  const t *ptrs0 = warp._data;
21344  cimg_forXYZ(res,x,y,z)
21345  *(ptrd++) = (T)_linear_atX(cimg::mod(x - (float)*(ptrs0++),(float)_width),y,z,c);
21346  }
21347  } break;
21348  case 1 : {
21349  cimg_forC(res,c) {
21350  const t *ptrs0 = warp._data;
21351  cimg_forXYZ(res,x,y,z)
21352  *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
21353  }
21354  } break;
21355  default : {
21356  cimg_forC(res,c) {
21357  const t *ptrs0 = warp._data;
21358  cimg_forXYZ(res,x,y,z)
21359  *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,0);
21360  }
21361  }
21362  } else switch (boundary_conditions) {
21363  case 2 : {
21364  cimg_forC(res,c) {
21365  const t *ptrs0 = warp._data;
21366  cimg_forXYZ(res,x,y,z)
21367  *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),y,z,c);
21368  }
21369  } break;
21370  case 1 : {
21371  cimg_forC(res,c) {
21372  const t *ptrs0 = warp._data;
21373  cimg_forXYZ(res,x,y,z)
21374  *(ptrd++) = _atX(x - (int)*(ptrs0++),y,z,c);
21375  }
21376  } break;
21377  default : {
21378  cimg_forC(res,c) {
21379  const t *ptrs0 = warp._data;
21380  cimg_forXYZ(res,x,y,z)
21381  *(ptrd++) = atX(x - (int)*(ptrs0++),y,z,c,0);
21382  }
21383  }
21384  }
21385  } else { // Absolute warp coordinates
21386  if (is_linear_interpolation) switch (boundary_conditions) {
21387  case 2 : {
21388  cimg_forC(res,c) {
21389  const t *ptrs0 = warp._data;
21390  cimg_forXYZ(res,x,y,z)
21391  *(ptrd++) = (T)_linear_atX(cimg::mod((float)*(ptrs0++),(float)_width),0,0,c);
21392  }
21393  } break;
21394  case 1 : {
21395  cimg_forC(res,c) {
21396  const t *ptrs0 = warp._data;
21397  cimg_forXYZ(res,x,y,z)
21398  *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
21399  }
21400  } break;
21401  default : {
21402  cimg_forC(res,c) {
21403  const t *ptrs0 = warp._data;
21404  cimg_forXYZ(res,x,y,z)
21405  *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,0);
21406  }
21407  }
21408  } else switch (boundary_conditions) {
21409  case 2 : {
21410  cimg_forC(res,c) {
21411  const t *ptrs0 = warp._data;
21412  cimg_forXYZ(res,x,y,z)
21413  *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),0,0,c);
21414  }
21415  } break;
21416  case 1 : {
21417  cimg_forC(res,c) {
21418  const t *ptrs0 = warp._data;
21419  cimg_forXYZ(res,x,y,z)
21420  *(ptrd++) = _atX((int)*(ptrs0++),0,0,c);
21421  }
21422  } break;
21423  default : {
21424  cimg_forC(res,c) {
21425  const t *ptrs0 = warp._data;
21426  cimg_forXYZ(res,x,y,z)
21427  *(ptrd++) = atX((int)*(ptrs0++),0,0,c,0);
21428  }
21429  }
21430  }
21431  }
21432  break;
21433 
21434  case 2 : // 2d warping
21435  if (is_relative) { // Relative warp coordinates
21436  if (is_linear_interpolation) switch (boundary_conditions) {
21437  case 2 : {
21438  cimg_forC(res,c) {
21439  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21440  cimg_forXYZ(res,x,y,z)
21441  *(ptrd++) = (T)_linear_atXY(cimg::mod(x - (float)*(ptrs0++),(float)_width),
21442  cimg::mod(y - (float)*(ptrs1++),(float)_height),z,c);
21443  }
21444  } break;
21445  case 1 : {
21446  cimg_forC(res,c) {
21447  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21448  cimg_forXYZ(res,x,y,z)
21449  *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
21450  }
21451  } break;
21452  default : {
21453  cimg_forC(res,c) {
21454  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21455  cimg_forXYZ(res,x,y,z)
21456  *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,0);
21457  }
21458  }
21459  } else switch (boundary_conditions) {
21460  case 2 : {
21461  cimg_forC(res,c) {
21462  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21463  cimg_forXYZ(res,x,y,z)
21464  *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),
21465  cimg::mod(y - (int)*(ptrs1++),(int)_height),z,c);
21466  }
21467  } break;
21468  case 1 : {
21469  cimg_forC(res,c) {
21470  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21471  cimg_forXYZ(res,x,y,z)
21472  *(ptrd++) = _atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c);
21473  }
21474  } break;
21475  default : {
21476  cimg_forC(res,c) {
21477  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21478  cimg_forXYZ(res,x,y,z)
21479  *(ptrd++) = atXY(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z,c,0);
21480  }
21481  }
21482  }
21483  } else { // Absolute warp coordinates
21484  if (is_linear_interpolation) switch (boundary_conditions) {
21485  case 2 : {
21486  cimg_forC(res,c) {
21487  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21488  cimg_forXYZ(res,x,y,z)
21489  *(ptrd++) = (T)_linear_atXY(cimg::mod((float)*(ptrs0++),(float)_width),
21490  cimg::mod((float)*(ptrs1++),(float)_height),0,c);
21491  }
21492  } break;
21493  case 1 : {
21494  cimg_forC(res,c) {
21495  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21496  cimg_forXYZ(res,x,y,z)
21497  *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
21498  }
21499  } break;
21500  default : {
21501  cimg_forC(res,c) {
21502  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21503  cimg_forXYZ(res,x,y,z)
21504  *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,0);
21505  }
21506  }
21507  } else switch (boundary_conditions) {
21508  case 2 : {
21509  cimg_forC(res,c) {
21510  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21511  cimg_forXYZ(res,x,y,z)
21512  *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
21513  cimg::mod((int)*(ptrs1++),(int)_height),0,c);
21514  }
21515  } break;
21516  case 1 : {
21517  cimg_forC(res,c) {
21518  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21519  cimg_forXYZ(res,x,y,z)
21520  *(ptrd++) = _atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c);
21521  }
21522  } break;
21523  default : {
21524  cimg_forC(res,c) {
21525  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1);
21526  cimg_forXYZ(res,x,y,z)
21527  *(ptrd++) = atXY((int)*(ptrs0++),(int)*(ptrs1++),0,c,0);
21528  }
21529  }
21530  }
21531  }
21532  break;
21533 
21534  default : // 3d warping
21535  if (is_relative) { // Relative warp coordinates
21536  if (is_linear_interpolation) switch (boundary_conditions) {
21537  case 2 : {
21538  cimg_forC(res,c) {
21539  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21540  cimg_forXYZ(res,x,y,z)
21541  *(ptrd++) = (T)_linear_atXYZ(cimg::mod(x - (float)*(ptrs0++),(float)_width),
21542  cimg::mod(y - (float)*(ptrs1++),(float)_height),
21543  cimg::mod(z - (float)*(ptrs2++),(float)_depth),c);
21544  }
21545  } break;
21546  case 1 : {
21547  cimg_forC(res,c) {
21548  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21549  cimg_forXYZ(res,x,y,z)
21550  *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
21551  }
21552  } break;
21553  default : {
21554  cimg_forC(res,c) {
21555  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21556  cimg_forXYZ(res,x,y,z)
21557  *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,0);
21558  }
21559  }
21560  } else switch (boundary_conditions) {
21561  case 2 : {
21562  cimg_forC(res,c) {
21563  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21564  cimg_forXYZ(res,x,y,z)
21565  *(ptrd++) = (*this)(cimg::mod(x - (int)*(ptrs0++),(int)_width),
21566  cimg::mod(y - (int)*(ptrs1++),(int)_height),
21567  cimg::mod(z - (int)*(ptrs2++),(int)_depth),c);
21568  }
21569  } break;
21570  case 1 : {
21571  cimg_forC(res,c) {
21572  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21573  cimg_forXYZ(res,x,y,z)
21574  *(ptrd++) = _atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c);
21575  }
21576  } break;
21577  default : {
21578  cimg_forC(res,c) {
21579  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21580  cimg_forXYZ(res,x,y,z)
21581  *(ptrd++) = atXYZ(x - (int)*(ptrs0++),y - (int)*(ptrs1++),z - (int)*(ptrs2++),c,0);
21582  }
21583  }
21584  }
21585  } else { // Absolute warp coordinates
21586  if (is_linear_interpolation) switch (boundary_conditions) {
21587  case 2 : {
21588  cimg_forC(res,c) {
21589  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21590  cimg_forXYZ(res,x,y,z)
21591  *(ptrd++) = (T)_linear_atXYZ(cimg::mod((float)*(ptrs0++),(float)_width),
21592  cimg::mod((float)*(ptrs1++),(float)_height),
21593  cimg::mod((float)*(ptrs2++),(float)_depth),c);
21594  }
21595  } break;
21596  case 1 : {
21597  cimg_forC(res,c) {
21598  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21599  cimg_forXYZ(res,x,y,z)
21600  *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
21601  }
21602  } break;
21603  default : {
21604  cimg_forC(res,c) {
21605  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21606  cimg_forXYZ(res,x,y,z)
21607  *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c,0);
21608  }
21609  }
21610  } else switch (boundary_conditions) {
21611  case 2 : {
21612  cimg_forC(res,c) {
21613  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21614  cimg_forXYZ(res,x,y,z)
21615  *(ptrd++) = (*this)(cimg::mod((int)*(ptrs0++),(int)_width),
21616  cimg::mod((int)*(ptrs1++),(int)_height),
21617  cimg::mod((int)*(ptrs2++),(int)_depth),c);
21618  }
21619  } break;
21620  case 1 : {
21621  cimg_forC(res,c) {
21622  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21623  cimg_forXYZ(res,x,y,z)
21624  *(ptrd++) = _atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c);
21625  }
21626  } break;
21627  default : {
21628  cimg_forC(res,c) {
21629  const t *ptrs0 = warp.data(0,0,0,0), *ptrs1 = warp.data(0,0,0,1), *ptrs2 = warp.data(0,0,0,2);
21630  cimg_forXYZ(res,x,y,z)
21631  *(ptrd++) = atXYZ((int)*(ptrs0++),(int)*(ptrs1++),(int)*(ptrs2++),c,0);
21632  }
21633  }
21634  }
21635  }
21636  }
21637  return res;
21638  }
21639 
21641 
21646  CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
21647  if (is_empty() || _depth<2) return +*this;
21648  const unsigned int
21649  _x0 = (x0>=_width)?_width - 1:x0,
21650  _y0 = (y0>=_height)?_height - 1:y0,
21651  _z0 = (z0>=_depth)?_depth - 1:z0;
21652  const CImg<T>
21653  img_xy = get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1),
21654  img_zy = get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).permute_axes("xzyc").resize(_depth,_height,1,-100,-1),
21655  img_xz = get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1);
21656  return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
21657  draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
21658  draw_image(0,img_xy._height,img_xz);
21659  }
21660 
21662  CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
21663  if (_depth<2) return *this;
21664  return get_projections2d(x0,y0,z0).move_to(*this);
21665  }
21666 
21668 
21679  CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
21680  const int x1, const int y1, const int z1, const int c1,
21681  const bool boundary_conditions=false) {
21682  return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
21683  }
21684 
21686  CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
21687  const int x1, const int y1, const int z1, const int c1,
21688  const bool boundary_conditions=false) const {
21689  if (is_empty())
21690  throw CImgInstanceException(_cimg_instance
21691  "crop(): Empty instance.",
21692  cimg_instance);
21693  const int
21694  nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
21695  ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
21696  nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
21697  nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
21698  CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
21699  if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum()) {
21700  if (boundary_conditions) cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0+x,ny0+y,nz0+z,nc0+c);
21701  else res.fill(0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
21702  } else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
21703  return res;
21704  }
21705 
21707  CImg<T>& crop(const int x0, const int y0, const int z0,
21708  const int x1, const int y1, const int z1,
21709  const bool boundary_conditions=false) {
21710  return crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,boundary_conditions);
21711  }
21712 
21714  CImg<T> get_crop(const int x0, const int y0, const int z0,
21715  const int x1, const int y1, const int z1,
21716  const bool boundary_conditions=false) const {
21717  return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum-1,boundary_conditions);
21718  }
21719 
21721  CImg<T>& crop(const int x0, const int y0,
21722  const int x1, const int y1,
21723  const bool boundary_conditions=false) {
21724  return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
21725  }
21726 
21728  CImg<T> get_crop(const int x0, const int y0,
21729  const int x1, const int y1,
21730  const bool boundary_conditions=false) const {
21731  return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
21732  }
21733 
21735  CImg<T>& crop(const int x0, const int x1, const bool boundary_conditions=false) {
21736  return crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,boundary_conditions);
21737  }
21738 
21740  CImg<T> get_crop(const int x0, const int x1, const bool boundary_conditions=false) const {
21741  return get_crop(x0,0,0,0,x1,_height-1,_depth-1,_spectrum-1,boundary_conditions);
21742  }
21743 
21745  CImg<T>& autocrop(const T value, const char *const axes="czyx") {
21746  if (is_empty()) return *this;
21747  for (const char *s = axes; *s; ++s) {
21748  const char axis = cimg::uncase(*s);
21749  const CImg<intT> coords = _autocrop(value,axis);
21750  if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels.
21751  else switch (axis) {
21752  case 'x' : {
21753  const int x0 = coords[0], x1 = coords[1];
21754  if (x0>=0 && x1>=0) crop(x0,x1);
21755  } break;
21756  case 'y' : {
21757  const int y0 = coords[0], y1 = coords[1];
21758  if (y0>=0 && y1>=0) crop(0,y0,_width-1,y1);
21759  } break;
21760  case 'z' : {
21761  const int z0 = coords[0], z1 = coords[1];
21762  if (z0>=0 && z1>=0) crop(0,0,z0,_width-1,_height-1,z1);
21763  } break;
21764  default : {
21765  const int c0 = coords[0], c1 = coords[1];
21766  if (c0>=0 && c1>=0) crop(0,0,0,c0,_width-1,_height-1,_depth-1,c1);
21767  }
21768  }
21769  }
21770  return *this;
21771  }
21772 
21774  CImg<T> get_autocrop(const T value, const char *const axes="czyx") const {
21775  return (+*this).autocrop(value,axes);
21776  }
21777 
21779 
21783  CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
21784  if (is_empty()) return *this;
21785  if (!color) { // Guess color.
21786  const CImg<T> col1 = get_vector_at(0,0,0);
21787  const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
21788  autocrop(col1,axes);
21789  if (_width==w && _height==h && _depth==d && _spectrum==s) {
21790  const CImg<T> col2 = get_vector_at(w-1,h-1,d-1);
21791  autocrop(col2,axes);
21792  }
21793  return *this;
21794  }
21795  for (const char *s = axes; *s; ++s) {
21796  const char axis = cimg::uncase(*s);
21797  switch (axis) {
21798  case 'x' : {
21799  int x0 = width(), x1 = -1;
21800  cimg_forC(*this,c) {
21801  const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
21802  const int nx0 = coords[0], nx1 = coords[1];
21803  if (nx0>=0 && nx1>=0) { x0 = cimg::min(x0,nx0); x1 = cimg::max(x1,nx1); }
21804  }
21805  if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
21806  } break;
21807  case 'y' : {
21808  int y0 = height(), y1 = -1;
21809  cimg_forC(*this,c) {
21810  const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
21811  const int ny0 = coords[0], ny1 = coords[1];
21812  if (ny0>=0 && ny1>=0) { y0 = cimg::min(y0,ny0); y1 = cimg::max(y1,ny1); }
21813  }
21814  if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width-1,y1);
21815  } break;
21816  default : {
21817  int z0 = depth(), z1 = -1;
21818  cimg_forC(*this,c) {
21819  const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
21820  const int nz0 = coords[0], nz1 = coords[1];
21821  if (nz0>=0 && nz1>=0) { z0 = cimg::min(z0,nz0); z1 = cimg::max(z1,nz1); }
21822  }
21823  if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width-1,_height-1,z1);
21824  }
21825  }
21826  }
21827  return *this;
21828  }
21829 
21831  CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
21832  return (+*this).autocrop(color,axes);
21833  }
21834 
21836  template<typename t> CImg<T>& autocrop(const CImg<t>& color, const char *const axes="zyx") {
21837  return get_autocrop(color,axes).move_to(*this);
21838  }
21839 
21841  template<typename t> CImg<T> get_autocrop(const CImg<t>& color, const char *const axes="zyx") const {
21842  return get_autocrop(color._data,axes);
21843  }
21844 
21845  CImg<intT> _autocrop(const T value, const char axis) const {
21846  CImg<intT> res;
21847  switch (cimg::uncase(axis)) {
21848  case 'x' : {
21849  int x0 = -1, x1 = -1;
21850  cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
21851  if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
21852  if (x0>=0) {
21853  for (int x = width()-1; x>=0; --x) cimg_forYZC(*this,y,z,c)
21854  if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
21855  }
21856  res = CImg<intT>::vector(x0,x1);
21857  } break;
21858  case 'y' : {
21859  int y0 = -1, y1 = -1;
21860  cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
21861  if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
21862  if (y0>=0) {
21863  for (int y = height()-1; y>=0; --y) cimg_forXZC(*this,x,z,c)
21864  if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
21865  }
21866  res = CImg<intT>::vector(y0,y1);
21867  } break;
21868  case 'z' : {
21869  int z0 = -1, z1 = -1;
21870  cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
21871  if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
21872  if (z0>=0) {
21873  for (int z = depth()-1; z>=0; --z) cimg_forXYC(*this,x,y,c)
21874  if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
21875  }
21876  res = CImg<intT>::vector(z0,z1);
21877  } break;
21878  default : {
21879  int c0 = -1, c1 = -1;
21880  cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
21881  if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
21882  if (c0>=0) {
21883  for (int c = spectrum()-1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
21884  if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
21885  }
21886  res = CImg<intT>::vector(c0,c1);
21887  }
21888  }
21889  return res;
21890  }
21891 
21893 
21896  CImg<T> get_column(const int x0) const {
21897  return get_columns(x0,x0);
21898  }
21899 
21901  CImg<T>& column(const int x0) {
21902  return columns(x0,x0);
21903  }
21904 
21906 
21910  CImg<T>& columns(const int x0, const int x1) {
21911  return get_columns(x0,x1).move_to(*this);
21912  }
21913 
21915  CImg<T> get_columns(const int x0, const int x1) const {
21916  return get_crop(x0,0,0,0,x1,height()-1,depth()-1,spectrum()-1);
21917  }
21918 
21920  CImg<T> get_row(const int y0) const {
21921  return get_rows(y0,y0);
21922  }
21923 
21925 
21928  CImg<T>& row(const int y0) {
21929  return rows(y0,y0);
21930  }
21931 
21933 
21937  CImg<T> get_rows(const int y0, const int y1) const {
21938  return get_crop(0,y0,0,0,width()-1,y1,depth()-1,spectrum()-1);
21939  }
21940 
21942  CImg<T>& rows(const int y0, const int y1) {
21943  return get_rows(y0,y1).move_to(*this);
21944  }
21945 
21947 
21950  CImg<T> get_slice(const int z0) const {
21951  return get_slices(z0,z0);
21952  }
21953 
21955  CImg<T>& slice(const int z0) {
21956  return slices(z0,z0);
21957  }
21958 
21960 
21964  CImg<T> get_slices(const int z0, const int z1) const {
21965  return get_crop(0,0,z0,0,width()-1,height()-1,z1,spectrum()-1);
21966  }
21967 
21969  CImg<T>& slices(const int z0, const int z1) {
21970  return get_slices(z0,z1).move_to(*this);
21971  }
21972 
21974 
21977  CImg<T> get_channel(const int c0) const {
21978  return get_channels(c0,c0);
21979  }
21980 
21982  CImg<T>& channel(const int c0) {
21983  return channels(c0,c0);
21984  }
21985 
21987 
21991  CImg<T> get_channels(const int c0, const int c1) const {
21992  return get_crop(0,0,0,c0,width()-1,height()-1,depth()-1,c1);
21993  }
21994 
21996  CImg<T>& channels(const int c0, const int c1) {
21997  return get_channels(c0,c1).move_to(*this);
21998  }
21999 
22001  CImg<floatT> get_streamline(const float x, const float y, const float z,
22002  const float L=256, const float dl=0.1f,
22003  const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
22004  const bool is_oriented_only=false) const {
22005  if (_spectrum!=2 && _spectrum!=3)
22006  throw CImgInstanceException(_cimg_instance
22007  "streamline(): Instance is not a 2d or 3d vector field.",
22008  cimg_instance);
22009  if (_spectrum==2) {
22010  if (is_oriented_only) {
22011  typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
22012  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,0.0f);
22013  } else {
22014  typename CImg<T>::_functor4d_streamline2d_directed func(*this);
22015  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,0.0f);
22016  }
22017  }
22018  if (is_oriented_only) {
22019  typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
22020  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f);
22021  }
22022  typename CImg<T>::_functor4d_streamline3d_directed func(*this);
22023  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,0,0,0,_width-1.0f,_height-1.0f,_depth-1.0f);
22024  }
22025 
22027 
22044  template<typename tfunc>
22045  static CImg<floatT> streamline(const tfunc& func,
22046  const float x, const float y, const float z,
22047  const float L=256, const float dl=0.1f,
22048  const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
22049  const bool is_oriented_only=false,
22050  const float x0=0, const float y0=0, const float z0=0,
22051  const float x1=0, const float y1=0, const float z1=0) {
22052  if (dl<=0)
22053  throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
22054  "(should be >0).",
22055  pixel_type(),
22056  dl);
22057 
22058  const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
22059  if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
22060  const unsigned int size_L = (unsigned int)cimg::round(L/dl+1);
22061  CImg<floatT> coordinates(size_L,3);
22062  const float dl2 = dl/2;
22063  float
22064  *ptr_x = coordinates.data(0,0),
22065  *ptr_y = coordinates.data(0,1),
22066  *ptr_z = coordinates.data(0,2),
22067  pu = (float)(dl*func(x,y,z,0)),
22068  pv = (float)(dl*func(x,y,z,1)),
22069  pw = (float)(dl*func(x,y,z,2)),
22070  X = x, Y = y, Z = z;
22071 
22072  switch (interpolation_type) {
22073  case 0 : { // Nearest integer interpolation.
22074  cimg_forX(coordinates,l) {
22075  *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
22076  const int
22077  xi = (int)(X>0?X+0.5f:X-0.5f),
22078  yi = (int)(Y>0?Y+0.5f:Y-0.5f),
22079  zi = (int)(Z>0?Z+0.5f:Z-0.5f);
22080  float
22081  u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
22082  v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
22083  w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
22084  if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
22085  if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
22086  if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
22087  }
22088  } break;
22089  case 1 : { // First-order interpolation.
22090  cimg_forX(coordinates,l) {
22091  *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
22092  float
22093  u = (float)(dl*func(X,Y,Z,0)),
22094  v = (float)(dl*func(X,Y,Z,1)),
22095  w = (float)(dl*func(X,Y,Z,2));
22096  if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
22097  if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
22098  if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
22099  }
22100  } break;
22101  case 2 : { // Second order interpolation.
22102  cimg_forX(coordinates,l) {
22103  *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
22104  float
22105  u0 = (float)(dl2*func(X,Y,Z,0)),
22106  v0 = (float)(dl2*func(X,Y,Z,1)),
22107  w0 = (float)(dl2*func(X,Y,Z,2));
22108  if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
22109  float
22110  u = (float)(dl*func(X+u0,Y+v0,Z+w0,0)),
22111  v = (float)(dl*func(X+u0,Y+v0,Z+w0,1)),
22112  w = (float)(dl*func(X+u0,Y+v0,Z+w0,2));
22113  if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
22114  if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
22115  if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
22116  }
22117  } break;
22118  default : { // Fourth order interpolation.
22119  cimg_forX(coordinates,x) {
22120  *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
22121  float
22122  u0 = (float)(dl2*func(X,Y,Z,0)),
22123  v0 = (float)(dl2*func(X,Y,Z,1)),
22124  w0 = (float)(dl2*func(X,Y,Z,2));
22125  if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
22126  float
22127  u1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,0)),
22128  v1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,1)),
22129  w1 = (float)(dl2*func(X+u0,Y+v0,Z+w0,2));
22130  if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
22131  float
22132  u2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,0)),
22133  v2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,1)),
22134  w2 = (float)(dl2*func(X+u1,Y+v1,Z+w1,2));
22135  if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
22136  float
22137  u3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,0)),
22138  v3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,1)),
22139  w3 = (float)(dl2*func(X+u2,Y+v2,Z+w2,2));
22140  if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
22141  const float
22142  u = (u0 + u3)/3 + (u1 + u2)/1.5f,
22143  v = (v0 + v3)/3 + (v1 + v2)/1.5f,
22144  w = (w0 + w3)/3 + (w1 + w2)/1.5f;
22145  if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
22146  if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
22147  }
22148  }
22149  }
22150  if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
22151  return coordinates;
22152  }
22153 
22155  static CImg<floatT> streamline(const char *const expression,
22156  const float x, const float y, const float z,
22157  const float L=256, const float dl=0.1f,
22158  const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
22159  const bool is_oriented_only=false,
22160  const float x0=0, const float y0=0, const float z0=0,
22161  const float x1=0, const float y1=0, const float z1=0) {
22162  _functor4d_streamline_expr func(expression);
22163  return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
22164  }
22165 
22167  const CImg<T>& ref;
22168  _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
22169  float operator()(const float x, const float y, const float z, const unsigned int c) const {
22170  return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
22171  }
22172  };
22173 
22175  const CImg<T>& ref;
22176  _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
22177  float operator()(const float x, const float y, const float z, const unsigned int c) const {
22178  return (float)ref._linear_atXYZ(x,y,z,c);
22179  }
22180  };
22181 
22183  const CImg<T>& ref;
22184  CImg<floatT> *pI;
22185  _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
22186  ~_functor4d_streamline2d_oriented() { delete pI; }
22187  float operator()(const float x, const float y, const float z, const unsigned int c) const {
22188 #define _cimg_vecalign2d(i,j) if (I(i,j,0)*I(0,0,0)+I(i,j,1)*I(0,0,1)<0) { I(i,j,0) = -I(i,j,0); I(i,j,1) = -I(i,j,1); }
22189  int
22190  xi = (int)x - (x>=0?0:1), nxi = xi + 1,
22191  yi = (int)y - (y>=0?0:1), nyi = yi + 1,
22192  zi = (int)z;
22193  const float
22194  dx = x - xi,
22195  dy = y - yi;
22196  if (c==0) {
22197  CImg<floatT>& I = *pI;
22198  if (xi<0) xi = 0; if (nxi<0) nxi = 0;
22199  if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
22200  if (yi<0) yi = 0; if (nyi<0) nyi = 0;
22201  if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
22202  I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1);
22203  I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1);
22204  I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
22205  I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1);
22206  _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
22207  }
22208  return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
22209  }
22210  };
22211 
22213  const CImg<T>& ref;
22214  CImg<floatT> *pI;
22215  _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
22216  ~_functor4d_streamline3d_oriented() { delete pI; }
22217  float operator()(const float x, const float y, const float z, const unsigned int c) const {
22218 #define _cimg_vecalign3d(i,j,k) if (I(i,j,k,0)*I(0,0,0,0)+I(i,j,k,1)*I(0,0,0,1)+I(i,j,k,2)*I(0,0,0,2)<0) { \
22219  I(i,j,k,0) = -I(i,j,k,0); I(i,j,k,1) = -I(i,j,k,1); I(i,j,k,2) = -I(i,j,k,2); }
22220  int
22221  xi = (int)x - (x>=0?0:1), nxi = xi + 1,
22222  yi = (int)y - (y>=0?0:1), nyi = yi + 1,
22223  zi = (int)z - (z>=0?0:1), nzi = zi + 1;
22224  const float
22225  dx = x - xi,
22226  dy = y - yi,
22227  dz = z - zi;
22228  if (c==0) {
22229  CImg<floatT>& I = *pI;
22230  if (xi<0) xi = 0; if (nxi<0) nxi = 0;
22231  if (xi>=ref.width()) xi = ref.width()-1; if (nxi>=ref.width()) nxi = ref.width()-1;
22232  if (yi<0) yi = 0; if (nyi<0) nyi = 0;
22233  if (yi>=ref.height()) yi = ref.height()-1; if (nyi>=ref.height()) nyi = ref.height()-1;
22234  if (zi<0) zi = 0; if (nzi<0) nzi = 0;
22235  if (zi>=ref.depth()) zi = ref.depth()-1; if (nzi>=ref.depth()) nzi = ref.depth()-1;
22236  I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1); I(0,0,0,2) = (float)ref(xi,yi,zi,2);
22237  I(1,0,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
22238  I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1); I(1,1,0,2) = (float)ref(nxi,nyi,zi,2);
22239  I(0,1,0,0) = (float)ref(xi,nyi,zi,0); I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,yi,zi,2);
22240  I(0,0,0,1) = (float)ref(xi,yi,nzi,0); I(0,0,0,1) = (float)ref(xi,yi,nzi,1); I(0,0,0,2) = (float)ref(xi,yi,nzi,2);
22241  I(1,0,0,1) = (float)ref(nxi,yi,nzi,0); I(1,0,0,1) = (float)ref(nxi,yi,nzi,1); I(1,0,0,2) = (float)ref(nxi,yi,nzi,2);
22242  I(1,1,0,1) = (float)ref(nxi,nyi,nzi,0); I(1,1,0,1) = (float)ref(nxi,nyi,nzi,1); I(1,1,0,2) = (float)ref(nxi,nyi,nzi,2);
22243  I(0,1,0,1) = (float)ref(xi,nyi,nzi,0); I(0,1,0,1) = (float)ref(xi,nyi,nzi,1); I(0,1,0,2) = (float)ref(xi,yi,nzi,2);
22244  _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
22245  _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
22246  }
22247  return (float)pI->_linear_atXYZ(dx,dy,dz,c);
22248  }
22249  };
22250 
22252  _cimg_math_parser *mp;
22253  ~_functor4d_streamline_expr() { delete mp; }
22254  _functor4d_streamline_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,"streamline"); }
22255  float operator()(const float x, const float y, const float z, const unsigned int c) const {
22256  return (float)mp->eval(x,y,z,c);
22257  }
22258  };
22259 
22261 
22268  CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
22269  const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
22270  const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
22271  if (beg>end || beg>=size() || end>=size())
22272  throw CImgArgumentException(_cimg_instance
22273  "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
22274  cimg_instance,
22275  x0,x1,y0,z0,c0);
22276 
22277  return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
22278  }
22279 
22281  const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
22282  const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
22283  const unsigned int beg = (unsigned int)offset(x0,y0,z0,c0), end = offset(x1,y0,z0,c0);
22284  if (beg>end || beg>=size() || end>=size())
22285  throw CImgArgumentException(_cimg_instance
22286  "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
22287  cimg_instance,
22288  x0,x1,y0,z0,c0);
22289 
22290  return CImg<T>(_data+beg,x1-x0+1,1,1,1,true);
22291  }
22292 
22294 
22300  CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
22301  const unsigned int z0=0, const unsigned int c0=0) {
22302  const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
22303  if (beg>end || beg>=size() || end>=size())
22304  throw CImgArgumentException(_cimg_instance
22305  "get_shared_rows(): Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).",
22306  cimg_instance,
22307  _width-1,y0,y1,z0,c0);
22308 
22309  return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
22310  }
22311 
22313  const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
22314  const unsigned int z0=0, const unsigned int c0=0) const {
22315  const unsigned int beg = offset(0,y0,z0,c0), end = offset(0,y1,z0,c0);
22316  if (beg>end || beg>=size() || end>=size())
22317  throw CImgArgumentException(_cimg_instance
22318  "get_shared_rows(): Invalid request of a shared-memory subset (0->%u,%u->%u,%u,%u).",
22319  cimg_instance,
22320  _width-1,y0,y1,z0,c0);
22321 
22322  return CImg<T>(_data+beg,_width,y1-y0+1,1,1,true);
22323  }
22324 
22326 
22331  CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
22332  return get_shared_rows(y0,y0,z0,c0);
22333  }
22334 
22336  const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
22337  return get_shared_rows(y0,y0,z0,c0);
22338  }
22339 
22341 
22346  CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
22347  const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
22348  if (beg>end || beg>=size() || end>=size())
22349  throw CImgArgumentException(_cimg_instance
22350  "get_shared_slices(): Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).",
22351  cimg_instance,
22352  _width-1,_height-1,z0,z1,c0);
22353 
22354  return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
22355  }
22356 
22358  const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
22359  const unsigned int beg = offset(0,0,z0,c0), end = offset(0,0,z1,c0);
22360  if (beg>end || beg>=size() || end>=size())
22361  throw CImgArgumentException(_cimg_instance
22362  "get_shared_slices(): Invalid request of a shared-memory subset (0->%u,0->%u,%u->%u,%u).",
22363  cimg_instance,
22364  _width-1,_height-1,z0,z1,c0);
22365 
22366  return CImg<T>(_data+beg,_width,_height,z1-z0+1,1,true);
22367  }
22368 
22370 
22374  CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
22375  return get_shared_slices(z0,z0,c0);
22376  }
22377 
22379  const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
22380  return get_shared_slices(z0,z0,c0);
22381  }
22382 
22384 
22388  CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
22389  const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
22390  if (beg>end || beg>=size() || end>=size())
22391  throw CImgArgumentException(_cimg_instance
22392  "get_shared_channels(): Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).",
22393  cimg_instance,
22394  _width-1,_height-1,_depth-1,c0,c1);
22395 
22396  return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
22397  }
22398 
22400  const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
22401  const unsigned int beg = offset(0,0,0,c0), end = offset(0,0,0,c1);
22402  if (beg>end || beg>=size() || end>=size())
22403  throw CImgArgumentException(_cimg_instance
22404  "get_shared_channels(): Invalid request of a shared-memory subset (0->%u,0->%u,0->%u,%u->%u).",
22405  cimg_instance,
22406  _width-1,_height-1,_depth-1,c0,c1);
22407 
22408  return CImg<T>(_data+beg,_width,_height,_depth,c1-c0+1,true);
22409  }
22410 
22412 
22415  CImg<T> get_shared_channel(const unsigned int c0) {
22416  return get_shared_channels(c0,c0);
22417  }
22418 
22420  const CImg<T> get_shared_channel(const unsigned int c0) const {
22421  return get_shared_channels(c0,c0);
22422  }
22423 
22426  return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
22427  }
22428 
22430  const CImg<T> get_shared() const {
22431  return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
22432  }
22433 
22435 
22443  CImgList<T> get_split(const char axis, const int nb=0) const {
22444  CImgList<T> res;
22445  const char _axis = cimg::uncase(axis);
22446 
22447  if (nb<=0) { // Split by bloc size.
22448  const unsigned int dp = (unsigned int)(nb?-nb:1);
22449  int p = 0;
22450  switch (_axis) {
22451  case 'x': {
22452  for (int pe=_width-dp; p<pe; p+=dp) get_crop(p,0,0,0,p+dp-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22453  get_crop(p,0,0,0,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22454  } break;
22455  case 'y': {
22456  for (int pe=_height-dp; p<pe; p+=dp) get_crop(0,p,0,0,_width-1,p+dp-1,_depth-1,_spectrum-1).move_to(res);
22457  get_crop(0,p,0,0,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22458  } break;
22459  case 'z': {
22460  for (int pe=_depth-dp; p<pe; p+=dp) get_crop(0,0,p,0,_width-1,_height-1,p+dp-1,_spectrum-1).move_to(res);
22461  get_crop(0,0,p,0,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22462  } break;
22463  default : {
22464  for (int pe=_spectrum-dp; p<pe; p+=dp) get_crop(0,0,0,p,_width-1,_height-1,_depth-1,p+dp-1).move_to(res);
22465  get_crop(0,0,0,p,_width-1,_height-1,_depth-1,_spectrum-1).move_to(res);
22466  }
22467  }
22468 
22469  } else { // Split by number of (non-homogeneous) blocs.
22470  const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
22471  if ((unsigned int)nb>siz)
22472  throw CImgArgumentException(_cimg_instance
22473  "get_split(): Instance cannot be split along %c-axis into %u blocs.",
22474  cimg_instance,
22475  axis,nb);
22476  int err = (int)siz;
22477  unsigned int _p = 0;
22478  switch (_axis) {
22479  case 'x' : {
22480  cimg_forX(*this,p) if ((err-=nb)<=0) {
22481  get_crop(_p,0,0,0,p,_height-1,_depth-1,_spectrum-1).move_to(res);
22482  err+=(int)siz;
22483  _p=p+1;
22484  }
22485  } break;
22486  case 'y' : {
22487  cimg_forY(*this,p) if ((err-=nb)<=0) {
22488  get_crop(0,_p,0,0,_width-1,p,_depth-1,_spectrum-1).move_to(res);
22489  err+=(int)siz;
22490  _p=p+1;
22491  }
22492  } break;
22493  case 'z' : {
22494  cimg_forZ(*this,p) if ((err-=nb)<=0) {
22495  get_crop(0,0,_p,0,_width-1,_height-1,p,_spectrum-1).move_to(res);
22496  err+=(int)siz;
22497  _p=p+1;
22498  }
22499  } break;
22500  default : {
22501  cimg_forC(*this,p) if ((err-=nb)<=0) {
22502  get_crop(0,0,0,_p,_width-1,_height-1,_depth-1,p).move_to(res);
22503  err+=(int)siz;
22504  _p=p+1;
22505  }
22506  }
22507  }
22508  }
22509  return res;
22510  }
22511 
22513 
22518  CImgList<T> get_split(const T value, const bool keep_values, const bool is_shared) const {
22519  CImgList<T> res;
22520  for (const T *ps = _data, *_ps = ps, *const pe = end(); ps<pe; ) {
22521  while (_ps<pe && *_ps==value) ++_ps;
22522  unsigned int siz = _ps - ps;
22523  if (siz && keep_values) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
22524  ps = _ps;
22525  while (_ps<pe && *_ps!=value) ++_ps;
22526  siz = _ps - ps;
22527  if (siz) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
22528  ps = _ps;
22529  }
22530  return res;
22531  }
22532 
22534 
22539  template<typename t>
22540  CImgList<T> get_split(const CImg<t>& values, const bool keep_values, const bool is_shared) const {
22541  if (!values) return CImgList<T>(*this);
22542  if (values.size()==1) return get_split(*values,keep_values,is_shared);
22543  CImgList<T> res;
22544  const t *pve = values.end();
22545  for (const T *ps = _data, *_ps = ps, *const pe = end(); ps<pe; ) {
22546 
22547  // Try to find match from current position.
22548  const t *pv = 0;
22549  do {
22550  pv = values._data;
22551  const T *__ps = _ps;
22552  while (__ps<pe && pv<pve && *__ps==(T)*pv) { ++__ps; ++pv; }
22553  if (pv==pve) _ps = __ps;
22554  } while (pv==pve);
22555  unsigned int siz = _ps - ps;
22556  if (siz && keep_values) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared); // If match found.
22557  ps = _ps;
22558 
22559  // Try to find non-match from current position.
22560  do {
22561  pv = values._data;
22562  while (_ps<pe && *_ps!=(T)*pv) ++_ps;
22563  if (_ps<pe) {
22564  const T *__ps = _ps + 1;
22565  ++pv;
22566  while (__ps<pe && pv<pve && *__ps==(T)*pv) { ++__ps; ++pv; }
22567  if (pv!=pve) _ps = __ps;
22568  }
22569  } while (_ps<pe && pv!=pve);
22570 
22571  // Here, EOF of match found.
22572  siz = _ps - ps;
22573  if (siz) res.insert(CImg<T>(ps,1,siz,1,1,is_shared),~0U,is_shared);
22574  ps = _ps;
22575  }
22576  return res;
22577  }
22578 
22580 
22585  template<typename t>
22586  CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
22587  if (is_empty()) return assign(img,false);
22588  if (!img) return *this;
22589  return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
22590  }
22591 
22593  CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
22594  if (is_empty()) return assign(img,false);
22595  if (!img) return *this;
22596  return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
22597  }
22598 
22600  template<typename t>
22601  CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
22602  if (is_empty()) return +img;
22603  if (!img) return +*this;
22604  return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
22605  }
22606 
22608  CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
22609  if (is_empty()) return +img;
22610  if (!img) return +*this;
22611  return CImgList<T>(*this,img,true).get_append(axis,align);
22612  }
22613 
22615  //---------------------------------------
22616  //
22618 
22619  //---------------------------------------
22620 
22622 
22629  template<typename t>
22630  CImg<T>& correlate(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
22631  if (is_empty() || !mask) return *this;
22632  return get_correlate(mask,boundary_conditions,is_normalized).move_to(*this);
22633  }
22634 
22636  template<typename t>
22637  CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& mask, const unsigned int boundary_conditions=1,
22638  const bool is_normalized=false) const {
22639  if (is_empty() || !mask) return *this;
22640  typedef _cimg_Ttfloat Ttfloat;
22641  CImg<Ttfloat> res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum));
22642  if (boundary_conditions && mask._width==mask._height && ((mask._depth==1 && mask._width<=5) || (mask._depth==mask._width && mask._width<=3))) {
22643  // A special optimization is done for 2x2, 3x3, 4x4, 5x5, 2x2x2 and 3x3x3 mask (with boundary_conditions=1)
22644  Ttfloat *ptrd = res._data;
22645  switch (mask._depth) {
22646  case 3 : {
22647  T I[27] = { 0 };
22648  cimg_forC(res,c) {
22649  const CImg<T> _img = get_shared_channel(c%_spectrum);
22650  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22651  if (is_normalized) {
22652  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22653  cimg_for3x3x3(_img,x,y,z,0,I,T) {
22654  const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] +
22655  I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
22656  I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] +
22657  I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
22658  I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
22659  I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
22660  I[18]*I[18] + I[19]*I[19] + I[20]*I[20] +
22661  I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
22662  I[24]*I[24] + I[25]*I[25] + I[26]*I[26]);
22663  *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] +
22664  I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
22665  I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] +
22666  I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22667  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
22668  I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
22669  I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] +
22670  I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
22671  I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26])/std::sqrt(N):0);
22672  }
22673  } else cimg_for3x3x3(_img,x,y,z,0,I,T)
22674  *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] +
22675  I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
22676  I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] +
22677  I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22678  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
22679  I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
22680  I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] +
22681  I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
22682  I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26]);
22683  }
22684  } break;
22685  case 2 : {
22686  T I[8] = { 0 };
22687  cimg_forC(res,c) {
22688  const CImg<T> _img = get_shared_channel(c%_spectrum);
22689  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22690  if (is_normalized) {
22691  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22692  cimg_for2x2x2(_img,x,y,z,0,I,T) {
22693  const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
22694  I[2]*I[2] + I[3]*I[3] +
22695  I[4]*I[4] + I[5]*I[5] +
22696  I[6]*I[6] + I[7]*I[7]);
22697  *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] +
22698  I[2]*_mask[2] + I[3]*_mask[3] +
22699  I[4]*_mask[4] + I[5]*_mask[5] +
22700  I[6]*_mask[6] + I[7]*_mask[7])/std::sqrt(N):0);
22701  }
22702  } else cimg_for2x2x2(_img,x,y,z,0,I,T)
22703  *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] +
22704  I[2]*_mask[2] + I[3]*_mask[3] +
22705  I[4]*_mask[4] + I[5]*_mask[5] +
22706  I[6]*_mask[6] + I[7]*_mask[7]);
22707  }
22708  } break;
22709  default :
22710  case 1 :
22711  switch (mask._width) {
22712  case 6 : {
22713  T I[36] = { 0 };
22714  cimg_forC(res,c) {
22715  const CImg<T> _img = get_shared_channel(c%_spectrum);
22716  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22717  if (is_normalized) {
22718  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22719  cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T) {
22720  const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] + I[ 5]*I[ 5] +
22721  I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
22722  I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15] + I[16]*I[16] + I[17]*I[17] +
22723  I[18]*I[18] + I[19]*I[19] + I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] +
22724  I[24]*I[24] + I[25]*I[25] + I[26]*I[26] + I[27]*I[27] + I[28]*I[28] + I[29]*I[29] +
22725  I[30]*I[30] + I[31]*I[31] + I[32]*I[32] + I[33]*I[33] + I[34]*I[34] + I[35]*I[35]);
22726  *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
22727  I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22728  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
22729  I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
22730  I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] +
22731  I[30]*_mask[30] + I[31]*_mask[31] + I[32]*_mask[32] + I[33]*_mask[33] + I[34]*_mask[34] + I[35]*_mask[35])/std::sqrt(N):0);
22732  }
22733  } else cimg_forZ(_img,z) cimg_for6x6(_img,x,y,z,0,I,T)
22734  *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] +
22735  I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22736  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] +
22737  I[18]*_mask[18] + I[19]*_mask[19] + I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] +
22738  I[24]*_mask[24] + I[25]*_mask[25] + I[26]*_mask[26] + I[27]*_mask[27] + I[28]*_mask[28] + I[29]*_mask[29] +
22739  I[30]*_mask[30] + I[31]*_mask[31] + I[32]*_mask[32] + I[33]*_mask[33] + I[34]*_mask[34] + I[35]*_mask[35]);
22740  }
22741  } break;
22742  case 5 : {
22743  T I[25] = { 0 };
22744  cimg_forC(res,c) {
22745  const CImg<T> _img = get_shared_channel(c%_spectrum);
22746  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22747  if (is_normalized) {
22748  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22749  cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T) {
22750  const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] + I[ 4]*I[ 4] +
22751  I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] + I[ 8]*I[ 8] + I[ 9]*I[ 9] +
22752  I[10]*I[10] + I[11]*I[11] + I[12]*I[12] + I[13]*I[13] + I[14]*I[14] +
22753  I[15]*I[15] + I[16]*I[16] + I[17]*I[17] + I[18]*I[18] + I[19]*I[19] +
22754  I[20]*I[20] + I[21]*I[21] + I[22]*I[22] + I[23]*I[23] + I[24]*I[24]);
22755  *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] +
22756  I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] +
22757  I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
22758  I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] +
22759  I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24])/std::sqrt(N):0);
22760  }
22761  } else cimg_forZ(_img,z) cimg_for5x5(_img,x,y,z,0,I,T)
22762  *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] + I[ 4]*_mask[ 4] +
22763  I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] + I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] +
22764  I[10]*_mask[10] + I[11]*_mask[11] + I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] +
22765  I[15]*_mask[15] + I[16]*_mask[16] + I[17]*_mask[17] + I[18]*_mask[18] + I[19]*_mask[19] +
22766  I[20]*_mask[20] + I[21]*_mask[21] + I[22]*_mask[22] + I[23]*_mask[23] + I[24]*_mask[24]);
22767  }
22768  } break;
22769  case 4 : {
22770  T I[16] = { 0 };
22771  cimg_forC(res,c) {
22772  const CImg<T> _img = get_shared_channel(c%_spectrum);
22773  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22774  if (is_normalized) {
22775  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22776  cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T) {
22777  const Ttfloat N = M*(I[ 0]*I[ 0] + I[ 1]*I[ 1] + I[ 2]*I[ 2] + I[ 3]*I[ 3] +
22778  I[ 4]*I[ 4] + I[ 5]*I[ 5] + I[ 6]*I[ 6] + I[ 7]*I[ 7] +
22779  I[ 8]*I[ 8] + I[ 9]*I[ 9] + I[10]*I[10] + I[11]*I[11] +
22780  I[12]*I[12] + I[13]*I[13] + I[14]*I[14] + I[15]*I[15]);
22781  *(ptrd++) = (Ttfloat)(N?(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] +
22782  I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] +
22783  I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22784  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15])/std::sqrt(N):0);
22785  }
22786  } else cimg_forZ(_img,z) cimg_for4x4(_img,x,y,z,0,I,T)
22787  *(ptrd++) = (Ttfloat)(I[ 0]*_mask[ 0] + I[ 1]*_mask[ 1] + I[ 2]*_mask[ 2] + I[ 3]*_mask[ 3] +
22788  I[ 4]*_mask[ 4] + I[ 5]*_mask[ 5] + I[ 6]*_mask[ 6] + I[ 7]*_mask[ 7] +
22789  I[ 8]*_mask[ 8] + I[ 9]*_mask[ 9] + I[10]*_mask[10] + I[11]*_mask[11] +
22790  I[12]*_mask[12] + I[13]*_mask[13] + I[14]*_mask[14] + I[15]*_mask[15]);
22791  }
22792  } break;
22793  case 3 : {
22794  T I[9] = { 0 };
22795  cimg_forC(res,c) {
22796  const CImg<T> _img = get_shared_channel(c%_spectrum);
22797  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22798  if (is_normalized) {
22799  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22800  cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T) {
22801  const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] + I[2]*I[2] +
22802  I[3]*I[3] + I[4]*I[4] + I[5]*I[5] +
22803  I[6]*I[6] + I[7]*I[7] + I[8]*I[8]);
22804  *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] +
22805  I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] +
22806  I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8])/std::sqrt(N):0);
22807  }
22808  } else cimg_forZ(_img,z) cimg_for3x3(_img,x,y,z,0,I,T)
22809  *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] + I[2]*_mask[2] +
22810  I[3]*_mask[3] + I[4]*_mask[4] + I[5]*_mask[5] +
22811  I[6]*_mask[6] + I[7]*_mask[7] + I[8]*_mask[8]);
22812  }
22813  } break;
22814  case 2 : {
22815  T I[4] = { 0 };
22816  cimg_forC(res,c) {
22817  const CImg<T> _img = get_shared_channel(c%_spectrum);
22818  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22819  if (is_normalized) {
22820  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22821  cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T) {
22822  const Ttfloat N = M*(I[0]*I[0] + I[1]*I[1] +
22823  I[2]*I[2] + I[3]*I[3]);
22824  *(ptrd++) = (Ttfloat)(N?(I[0]*_mask[0] + I[1]*_mask[1] +
22825  I[2]*_mask[2] + I[3]*_mask[3])/std::sqrt(N):0);
22826  }
22827  } else cimg_forZ(_img,z) cimg_for2x2(_img,x,y,z,0,I,T)
22828  *(ptrd++) = (Ttfloat)(I[0]*_mask[0] + I[1]*_mask[1] +
22829  I[2]*_mask[2] + I[3]*_mask[3]);
22830  }
22831  } break;
22832  case 1 :
22833  if (is_normalized) res.fill(1);
22834  else cimg_forC(res,c) {
22835  const CImg<T> _img = get_shared_channel(c%_spectrum);
22836  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22837  res.get_shared_channel(c).assign(_img)*=_mask[0];
22838  }
22839  break;
22840  }
22841  }
22842  } else { // Generic version for other masks and borders conditions.
22843  const int
22844  mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
22845  mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
22846  mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
22847  cimg_forC(res,c) {
22848  const CImg<T> _img = get_shared_channel(c%_spectrum);
22849  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22850  if (is_normalized) { // Normalized correlation.
22851  const Ttfloat _M = (Ttfloat)_mask.magnitude(2), M = _M*_M;
22852  for (int z = mz1; z<mze; ++z)
22853  for (int y = my1; y<mye; ++y)
22854  for (int x = mx1; x<mxe; ++x) {
22855  Ttfloat val = 0, N = 0;
22856  for (int zm = -mz1; zm<=mz2; ++zm)
22857  for (int ym = -my1; ym<=my2; ++ym)
22858  for (int xm = -mx1; xm<=mx2; ++xm) {
22859  const Ttfloat _val = (Ttfloat)_img(x+xm,y+ym,z+zm);
22860  val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
22861  N+=_val*_val;
22862  }
22863  N*=M;
22864  res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
22865  }
22866  if (boundary_conditions)
22867  cimg_forYZ(res,y,z)
22868  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22869  Ttfloat val = 0, N = 0;
22870  for (int zm = -mz1; zm<=mz2; ++zm)
22871  for (int ym = -my1; ym<=my2; ++ym)
22872  for (int xm = -mx1; xm<=mx2; ++xm) {
22873  const Ttfloat _val = (Ttfloat)_img._atXYZ(x+xm,y+ym,z+zm);
22874  val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
22875  N+=_val*_val;
22876  }
22877  N*=M;
22878  res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
22879  }
22880  else
22881  cimg_forYZ(res,y,z)
22882  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22883  Ttfloat val = 0, N = 0;
22884  for (int zm = -mz1; zm<=mz2; ++zm)
22885  for (int ym = -my1; ym<=my2; ++ym)
22886  for (int xm = -mx1; xm<=mx2; ++xm) {
22887  const Ttfloat _val = (Ttfloat)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
22888  val+=_val*_mask(mx1+xm,my1+ym,mz1+zm);
22889  N+=_val*_val;
22890  }
22891  N*=M;
22892  res(x,y,z,c) = (Ttfloat)(N?val/std::sqrt(N):0);
22893  }
22894  } else { // Classical correlation.
22895  for (int z = mz1; z<mze; ++z)
22896  for (int y = my1; y<mye; ++y)
22897  for (int x = mx1; x<mxe; ++x) {
22898  Ttfloat val = 0;
22899  for (int zm = -mz1; zm<=mz2; ++zm)
22900  for (int ym = -my1; ym<=my2; ++ym)
22901  for (int xm = -mx1; xm<=mx2; ++xm)
22902  val+=_img(x+xm,y+ym,z+zm)*_mask(mx1+xm,my1+ym,mz1+zm);
22903  res(x,y,z,c) = (Ttfloat)val;
22904  }
22905  if (boundary_conditions)
22906  cimg_forYZ(res,y,z)
22907  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22908  Ttfloat val = 0;
22909  for (int zm = -mz1; zm<=mz2; ++zm)
22910  for (int ym = -my1; ym<=my2; ++ym)
22911  for (int xm = -mx1; xm<=mx2; ++xm)
22912  val+=_img._atXYZ(x+xm,y+ym,z+zm)*_mask(mx1+xm,my1+ym,mz1+zm);
22913  res(x,y,z,c) = (Ttfloat)val;
22914  }
22915  else
22916  cimg_forYZ(res,y,z)
22917  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22918  Ttfloat val = 0;
22919  for (int zm = -mz1; zm<=mz2; ++zm)
22920  for (int ym = -my1; ym<=my2; ++ym)
22921  for (int xm = -mx1; xm<=mx2; ++xm)
22922  val+=_img.atXYZ(x+xm,y+ym,z+zm,0,0)*_mask(mx1+xm,my1+ym,mz1+zm);
22923  res(x,y,z,c) = (Ttfloat)val;
22924  }
22925  }
22926  }
22927  }
22928  return res;
22929  }
22930 
22932 
22939  template<typename t>
22940  CImg<T>& convolve(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
22941  if (is_empty() || !mask) return *this;
22942  return get_convolve(mask,boundary_conditions,is_normalized).move_to(*this);
22943  }
22944 
22946  template<typename t>
22947  CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& mask, const unsigned int boundary_conditions=1,
22948  const bool is_normalized=false) const {
22949  if (is_empty() || !mask) return *this;
22950  return get_correlate(CImg<t>(mask._data,mask.size(),1,1,1,true).get_mirror('x').resize(mask,-1),boundary_conditions,is_normalized);
22951  }
22952 
22954 
22959  template<typename t>
22960  CImg<T>& erode(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
22961  if (is_empty() || !mask) return *this;
22962  return get_erode(mask,boundary_conditions,is_normalized).move_to(*this);
22963  }
22964 
22966  template<typename t>
22967  CImg<_cimg_Tt> get_erode(const CImg<t>& mask, const unsigned int boundary_conditions=1,
22968  const bool is_normalized=false) const {
22969  if (is_empty() || !mask) return *this;
22970  typedef _cimg_Tt Tt;
22971  CImg<Tt> res(_width,_height,_depth,cimg::max(_spectrum,mask._spectrum));
22972  const int
22973  mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
22974  mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
22975  mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
22976  cimg_forC(*this,c) {
22977  const CImg<T> _img = get_shared_channel(c%_spectrum);
22978  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
22979  if (is_normalized) { // Normalized erosion.
22980  for (int z = mz1; z<mze; ++z)
22981  for (int y = my1; y<mye; ++y)
22982  for (int x = mx1; x<mxe; ++x) {
22983  Tt min_val = cimg::type<Tt>::max();
22984  for (int zm = -mz1; zm<=mz2; ++zm)
22985  for (int ym = -my1; ym<=my2; ++ym)
22986  for (int xm = -mx1; xm<=mx2; ++xm) {
22987  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
22988  const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) + mval);
22989  if (mval && cval<min_val) min_val = cval;
22990  }
22991  res(x,y,z,c) = min_val;
22992  }
22993  if (boundary_conditions)
22994  cimg_forYZ(res,y,z)
22995  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
22996  Tt min_val = cimg::type<Tt>::max();
22997  for (int zm = -mz1; zm<=mz2; ++zm)
22998  for (int ym = -my1; ym<=my2; ++ym)
22999  for (int xm = -mx1; xm<=mx2; ++xm) {
23000  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
23001  const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) + mval);
23002  if (mval && cval<min_val) min_val = cval;
23003  }
23004  res(x,y,z,c) = min_val;
23005  }
23006  else
23007  cimg_forYZ(res,y,z)
23008  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23009  Tt min_val = cimg::type<Tt>::max();
23010  for (int zm = -mz1; zm<=mz2; ++zm)
23011  for (int ym = -my1; ym<=my2; ++ym)
23012  for (int xm = -mx1; xm<=mx2; ++xm) {
23013  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
23014  const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) + mval);
23015  if (mval && cval<min_val) min_val = cval;
23016  }
23017  res(x,y,z,c) = min_val;
23018  }
23019  } else { // Classical erosion.
23020  for (int z = mz1; z<mze; ++z)
23021  for (int y = my1; y<mye; ++y)
23022  for (int x = mx1; x<mxe; ++x) {
23023  Tt min_val = cimg::type<Tt>::max();
23024  for (int zm = -mz1; zm<=mz2; ++zm)
23025  for (int ym = -my1; ym<=my2; ++ym)
23026  for (int xm = -mx1; xm<=mx2; ++xm) {
23027  const Tt cval = (Tt)_img(x+xm,y+ym,z+zm);
23028  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
23029  }
23030  res(x,y,z,c) = min_val;
23031  }
23032  if (boundary_conditions)
23033  cimg_forYZ(res,y,z)
23034  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23035  Tt min_val = cimg::type<Tt>::max();
23036  for (int zm = -mz1; zm<=mz2; ++zm)
23037  for (int ym = -my1; ym<=my2; ++ym)
23038  for (int xm = -mx1; xm<=mx2; ++xm) {
23039  const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm);
23040  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
23041  }
23042  res(x,y,z,c) = min_val;
23043  }
23044  else
23045  cimg_forYZ(res,y,z)
23046  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23047  Tt min_val = cimg::type<Tt>::max();
23048  for (int zm = -mz1; zm<=mz2; ++zm)
23049  for (int ym = -my1; ym<=my2; ++ym)
23050  for (int xm = -mx1; xm<=mx2; ++xm) {
23051  const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
23052  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval<min_val) min_val = cval;
23053  }
23054  res(x,y,z,c) = min_val;
23055  }
23056  }
23057  }
23058  return res;
23059  }
23060 
23062 
23067  CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
23068  if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
23069  if (sx>1 && _width>1) { // Along X-axis.
23070  const int L = width(), off = 1, s = (int)sx, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23071  CImg<T> buf(L);
23072  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23073  cimg_forYZC(*this,y,z,c) {
23074  const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23075  ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23076  for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23077  for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23078  for (int p = L - s - 1; p>0; --p) {
23079  const T val = *ptrs; ptrs+=off;
23080  if (is_first) {
23081  const T *nptrs = ptrs - off; cur = val;
23082  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
23083  nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
23084  } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23085  *(ptrd++) = cur;
23086  }
23087  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23088  for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
23089  for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; }
23090  T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23091  }
23092  }
23093 
23094  if (sy>1 && _height>1) { // Along Y-axis.
23095  const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23096  CImg<T> buf(L);
23097  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23098  cimg_forXZC(*this,x,z,c) {
23099  const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23100  ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23101  for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23102  for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23103  for (int p = L - s - 1; p>0; --p) {
23104  const T val = *ptrs; ptrs+=off;
23105  if (is_first) {
23106  const T *nptrs = ptrs - off; cur = val;
23107  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
23108  nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
23109  } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23110  *(ptrd++) = cur;
23111  }
23112  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23113  for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
23114  for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; }
23115  T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23116  }
23117  }
23118 
23119  if (sz>1 && _depth>1) { // Along Z-axis.
23120  const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23121  CImg<T> buf(L);
23122  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23123  cimg_forXYC(*this,x,y,c) {
23124  const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23125  ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23126  for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23127  for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23128  for (int p = L - s - 1; p>0; --p) {
23129  const T val = *ptrs; ptrs+=off;
23130  if (is_first) {
23131  const T *nptrs = ptrs - off; cur = val;
23132  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
23133  nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
23134  } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23135  *(ptrd++) = cur;
23136  }
23137  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23138  for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val<cur) cur = val; } *(ptrd--) = cur;
23139  for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur; }
23140  T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23141  }
23142  }
23143  return *this;
23144  }
23145 
23147  CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
23148  return (+*this).erode(sx,sy,sz);
23149  }
23150 
23152 
23155  CImg<T>& erode(const unsigned int s) {
23156  return erode(s,s,s);
23157  }
23158 
23160  CImg<T> get_erode(const unsigned int s) const {
23161  return (+*this).erode(s);
23162  }
23163 
23165 
23170  template<typename t>
23171  CImg<T>& dilate(const CImg<t>& mask, const unsigned int boundary_conditions=1, const bool is_normalized=false) {
23172  if (is_empty() || !mask) return *this;
23173  return get_dilate(mask,boundary_conditions,is_normalized).move_to(*this);
23174  }
23175 
23177  template<typename t>
23178  CImg<_cimg_Tt> get_dilate(const CImg<t>& mask, const unsigned int boundary_conditions=1,
23179  const bool is_normalized=false) const {
23180  if (is_empty() || !mask) return *this;
23181  typedef _cimg_Tt Tt;
23182  CImg<Tt> res(_width,_height,_depth,_spectrum);
23183  const int
23184  mx2 = mask.width()/2, my2 = mask.height()/2, mz2 = mask.depth()/2,
23185  mx1 = mx2 - 1 + (mask.width()%2), my1 = my2 - 1 + (mask.height()%2), mz1 = mz2 - 1 + (mask.depth()%2),
23186  mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2;
23187  cimg_forC(*this,c) {
23188  const CImg<T> _img = get_shared_channel(c%_spectrum);
23189  const CImg<t> _mask = mask.get_shared_channel(c%mask._spectrum);
23190  if (is_normalized) { // Normalized dilation.
23191  for (int z = mz1; z<mze; ++z)
23192  for (int y = my1; y<mye; ++y)
23193  for (int x = mx1; x<mxe; ++x) {
23194  Tt max_val = cimg::type<Tt>::min();
23195  for (int zm = -mz1; zm<=mz2; ++zm)
23196  for (int ym = -my1; ym<=my2; ++ym)
23197  for (int xm = -mx1; xm<=mx2; ++xm) {
23198  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
23199  const Tt cval = (Tt)(_img(x+xm,y+ym,z+zm) - mval);
23200  if (mval && cval>max_val) max_val = cval;
23201  }
23202  res(x,y,z,c) = max_val;
23203  }
23204  if (boundary_conditions)
23205  cimg_forYZ(res,y,z)
23206  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23207  Tt max_val = cimg::type<Tt>::min();
23208  for (int zm = -mz1; zm<=mz2; ++zm)
23209  for (int ym = -my1; ym<=my2; ++ym)
23210  for (int xm = -mx1; xm<=mx2; ++xm) {
23211  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
23212  const Tt cval = (Tt)(_img._atXYZ(x+xm,y+ym,z+zm) - mval);
23213  if (mval && cval>max_val) max_val = cval;
23214  }
23215  res(x,y,z,c) = max_val;
23216  }
23217  else
23218  cimg_forYZ(*this,y,z)
23219  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23220  Tt max_val = cimg::type<Tt>::min();
23221  for (int zm = -mz1; zm<=mz2; ++zm)
23222  for (int ym = -my1; ym<=my2; ++ym)
23223  for (int xm = -mx1; xm<=mx2; ++xm) {
23224  const t mval = _mask(mx1+xm,my1+ym,mz1+zm);
23225  const Tt cval = (Tt)(_img.atXYZ(x+xm,y+ym,z+zm,0,0) - mval);
23226  if (mval && cval>max_val) max_val = cval;
23227  }
23228  res(x,y,z,c) = max_val;
23229  }
23230  } else { // Classical dilation.
23231  for (int z = mz1; z<mze; ++z)
23232  for (int y = my1; y<mye; ++y)
23233  for (int x = mx1; x<mxe; ++x) {
23234  Tt max_val = cimg::type<Tt>::min();
23235  for (int zm = -mz1; zm<=mz2; ++zm)
23236  for (int ym = -my1; ym<=my2; ++ym)
23237  for (int xm = -mx1; xm<=mx2; ++xm) {
23238  const Tt cval = (Tt)_img(x+xm,y+ym,z+zm);
23239  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
23240  }
23241  res(x,y,z,c) = max_val;
23242  }
23243  if (boundary_conditions)
23244  cimg_forYZ(res,y,z)
23245  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23246  Tt max_val = cimg::type<Tt>::min();
23247  for (int zm = -mz1; zm<=mz2; ++zm)
23248  for (int ym = -my1; ym<=my2; ++ym)
23249  for (int xm = -mx1; xm<=mx2; ++xm) {
23250  const T cval = (Tt)_img._atXYZ(x+xm,y+ym,z+zm);
23251  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
23252  }
23253  res(x,y,z,c) = max_val;
23254  }
23255  else
23256  cimg_forYZ(res,y,z)
23257  for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1-1 || x>=mxe)?++x:(x=mxe))) {
23258  Tt max_val = cimg::type<Tt>::min();
23259  for (int zm = -mz1; zm<=mz2; ++zm)
23260  for (int ym = -my1; ym<=my2; ++ym)
23261  for (int xm = -mx1; xm<=mx2; ++xm) {
23262  const T cval = (Tt)_img.atXYZ(x+xm,y+ym,z+zm,0,0);
23263  if (_mask(mx1+xm,my1+ym,mz1+zm) && cval>max_val) max_val = cval;
23264  }
23265  res(x,y,z,c) = max_val;
23266  }
23267  }
23268  }
23269  return res;
23270  }
23271 
23273 
23278  CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
23279  if (is_empty() || (sx==1 && sy==1 && sz==1)) return *this;
23280  if (sx>1 && _width>1) { // Along X-axis.
23281  const int L = width(), off = 1, s = (int)sx, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23282  CImg<T> buf(L);
23283  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23284  cimg_forYZC(*this,y,z,c) {
23285  const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23286  ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23287  for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23288  for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23289  for (int p = L - s - 1; p>0; --p) {
23290  const T val = *ptrs; ptrs+=off;
23291  if (is_first) {
23292  const T *nptrs = ptrs - off; cur = val;
23293  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
23294  nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
23295  } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23296  *(ptrd++) = cur;
23297  }
23298  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23299  for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
23300  for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; }
23301  T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23302  }
23303  }
23304 
23305  if (sy>1 && _height>1) { // Along Y-axis.
23306  const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23307  CImg<T> buf(L);
23308  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23309  cimg_forXZC(*this,x,z,c) {
23310  const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23311  ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23312  for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23313  for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23314  for (int p = L - s - 1; p>0; --p) {
23315  const T val = *ptrs; ptrs+=off;
23316  if (is_first) {
23317  const T *nptrs = ptrs - off; cur = val;
23318  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
23319  nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
23320  } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23321  *(ptrd++) = cur;
23322  }
23323  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23324  for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
23325  for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; }
23326  T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23327  }
23328  }
23329 
23330  if (sz>1 && _depth>1) { // Along Z-axis.
23331  const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
23332  CImg<T> buf(L);
23333  T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
23334  cimg_forXYC(*this,x,y,c) {
23335  const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + L*off - off;
23336  ptrd = buf._data; T cur = *ptrs; ptrs+=off; bool is_first = true;
23337  for (int p = s2-1; p>0 && ptrs<=ptrse; --p) { const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }} *(ptrd++) = cur;
23338  for (int p = s1; p>0 && ptrd<=ptrde; --p) { const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; } *(ptrd++) = cur; }
23339  for (int p = L - s - 1; p>0; --p) {
23340  const T val = *ptrs; ptrs+=off;
23341  if (is_first) {
23342  const T *nptrs = ptrs - off; cur = val;
23343  for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
23344  nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
23345  } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
23346  *(ptrd++) = cur;
23347  }
23348  ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
23349  for (int p = s1; p>0 && ptrs>=ptrsb; --p) { const T val = *ptrs; ptrs-=off; if (val>cur) cur = val; } *(ptrd--) = cur;
23350  for (int p = s2-1; p>0 && ptrd>=ptrdb; --p) { const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur; }
23351  T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
23352  }
23353  }
23354  return *this;
23355  }
23356 
23358  CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
23359  return (+*this).dilate(sx,sy,sz);
23360  }
23361 
23363 
23366  CImg<T>& dilate(const unsigned int s) {
23367  return dilate(s,s,s);
23368  }
23369 
23371  CImg<T> get_dilate(const unsigned int s) const {
23372  return (+*this).dilate(s);
23373  }
23374 
23376 
23381  template<typename t>
23382  CImg<T>& watershed(const CImg<t>& priority, const bool fill_lines=true) {
23383  if (is_empty()) return *this;
23384  if (!is_sameXYZ(priority))
23385  throw CImgArgumentException(_cimg_instance
23386  "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) have different dimensions.",
23387  cimg_instance,priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
23388  if (_spectrum!=1) { cimg_forC(*this,c) get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum),fill_lines); return *this; }
23389 
23390  CImg<boolT> in_queue(_width,_height,_depth,1,0);
23392  unsigned int sizeQ = 0;
23393 
23394  // Find seed points and insert them in priority queue.
23395  const T *ptrs = _data;
23396  cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) {
23397  if (x-1>=0 && !(*this)(x-1,y,z)) Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
23398  if (x+1<width() && !(*this)(x+1,y,z)) Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
23399  if (y-1>=0 && !(*this)(x,y-1,z)) Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
23400  if (y+1<height() && !(*this)(x,y+1,z)) Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
23401  if (z-1>=0 && !(*this)(x,y,z-1)) Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
23402  if (z+1<depth() && !(*this)(x,y,z+1)) Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
23403  }
23404 
23405  // Start watershed computation.
23406  while (sizeQ) {
23407 
23408  // Get and remove point with maximal priority from the queue.
23409  const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
23410  Q._priority_queue_remove(sizeQ);
23411 
23412  // Check labels of the neighbors.
23413  bool is_same_label = true;
23414  unsigned int label = 0;
23415  if (x-1>=0) {
23416  if ((*this)(x-1,y,z)) { if (!label) label = (unsigned int)(*this)(x-1,y,z); else if (label!=(*this)(x-1,y,z)) is_same_label = false; }
23417  else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
23418  }
23419  if (x+1<width()) {
23420  if ((*this)(x+1,y,z)) { if (!label) label = (unsigned int)(*this)(x+1,y,z); else if (label!=(*this)(x+1,y,z)) is_same_label = false; }
23421  else Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
23422  }
23423  if (y-1>=0) {
23424  if ((*this)(x,y-1,z)) { if (!label) label = (unsigned int)(*this)(x,y-1,z); else if (label!=(*this)(x,y-1,z)) is_same_label = false; }
23425  else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
23426  }
23427  if (y+1<height()) {
23428  if ((*this)(x,y+1,z)) { if (!label) label = (unsigned int)(*this)(x,y+1,z); else if (label!=(*this)(x,y+1,z)) is_same_label = false; }
23429  else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
23430  }
23431  if (z-1>=0) {
23432  if ((*this)(x,y,z-1)) { if (!label) label = (unsigned int)(*this)(x,y,z-1); else if (label!=(*this)(x,y,z-1)) is_same_label = false; }
23433  else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
23434  }
23435  if (z+1<depth()) {
23436  if ((*this)(x,y,z+1)) { if (!label) label = (unsigned int)(*this)(x,y,z+1); else if (label!=(*this)(x,y,z+1)) is_same_label = false; }
23437  else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
23438  }
23439  if (is_same_label) (*this)(x,y,z) = label;
23440  }
23441 
23442  // Fill lines.
23443  if (fill_lines) {
23444 
23445  // Sort all non-labeled pixels with labeled neighbors.
23446  in_queue = false;
23447  const T *ptrs = _data;
23448  cimg_forXYZ(*this,x,y,z) if (!*(ptrs++) &&
23449  ((x-1>=0 && (*this)(x-1,y,z)) || (x+1<width() && (*this)(x+1,y,z)) ||
23450  (y-1>=0 && (*this)(x,y-1,z)) || (y+1<height() && (*this)(x,y+1,z)) ||
23451  (z-1>=0 && (*this)(x,y,z-1)) || (z+1>depth() && (*this)(x,y,z+1))))
23452  Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z),x,y,z);
23453 
23454  // Start line filling process.
23455  while (sizeQ) {
23456  const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
23457  Q._priority_queue_remove(sizeQ);
23458  t pmax = cimg::type<t>::min();
23459  int xmax = 0, ymax = 0, zmax = 0;
23460  if (x-1>=0) {
23461  if ((*this)(x-1,y,z)) { if (priority(x-1,y,z)>pmax) { pmax = priority(x-1,y,z); xmax = x-1; ymax = y; zmax = z; }}
23462  else Q._priority_queue_insert(in_queue,sizeQ,priority(x-1,y,z),x-1,y,z);
23463  }
23464  if (x+1<width()) {
23465  if ((*this)(x+1,y,z)) { if (priority(x+1,y,z)>pmax) { pmax = priority(x+1,y,z); xmax = x+1; ymax = y; zmax = z; }}
23466  else Q._priority_queue_insert(in_queue,sizeQ,priority(x+1,y,z),x+1,y,z);
23467  }
23468  if (y-1>=0) {
23469  if ((*this)(x,y-1,z)) { if (priority(x,y-1,z)>pmax) { pmax = priority(x,y-1,z); xmax = x; ymax = y-1; zmax = z; }}
23470  else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y-1,z),x,y-1,z);
23471  }
23472  if (y+1<height()) {
23473  if ((*this)(x,y+1,z)) { if (priority(x,y+1,z)>pmax) { pmax = priority(x,y+1,z); xmax = x; ymax = y+1; zmax = z; }}
23474  else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y+1,z),x,y+1,z);
23475  }
23476  if (z-1>=0) {
23477  if ((*this)(x,y,z-1)) { if (priority(x,y,z-1)>pmax) { pmax = priority(x,y,z-1); xmax = x; ymax = y; zmax = z-1; }}
23478  else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z-1),x,y,z-1);
23479  }
23480  if (z+1<depth()) {
23481  if ((*this)(x,y,z+1)) { if (priority(x,y,z+1)>pmax) { pmax = priority(x,y,z+1); xmax = x; ymax = y; zmax = z+1; }}
23482  else Q._priority_queue_insert(in_queue,sizeQ,priority(x,y,z+1),x,y,z+1);
23483  }
23484  (*this)(x,y,z) = (*this)(xmax,ymax,zmax);
23485  }
23486  }
23487  return *this;
23488  }
23489 
23491  template<typename t>
23492  CImg<T> get_watershed(const CImg<t>& priority, const bool fill_lines=true) const {
23493  return (+*this).watershed(priority,fill_lines);
23494  }
23495 
23496  // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
23497  template<typename t>
23498  bool _priority_queue_insert(CImg<boolT>& in_queue, unsigned int& siz, const t value, const unsigned int x, const unsigned int y, const unsigned int z) {
23499  if (in_queue(x,y,z)) return false;
23500  in_queue(x,y,z) = true;
23501  if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
23502  (*this)(siz-1,0) = (T)value; (*this)(siz-1,1) = (T)x; (*this)(siz-1,2) = (T)y; (*this)(siz-1,3) = (T)z;
23503  for (unsigned int pos = siz - 1, par = 0; pos && value>(*this)(par=(pos+1)/2-1,0); pos = par) {
23504  cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
23505  cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
23506  }
23507  return true;
23508  }
23509 
23510  CImg<T>& _priority_queue_remove(unsigned int& siz) {
23511  (*this)(0,0) = (*this)(--siz,0); (*this)(0,1) = (*this)(siz,1); (*this)(0,2) = (*this)(siz,2); (*this)(0,3) = (*this)(siz,3);
23512  const float value = (*this)(0,0);
23513  for (unsigned int pos = 0, left = 0, right = 0;
23514  ((right=2*(pos+1),(left=right-1))<siz && value<(*this)(left,0)) || (right<siz && value<(*this)(right,0));) {
23515  if (right<siz) {
23516  if ((*this)(left,0)>(*this)(right,0)) {
23517  cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
23518  cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
23519  pos = left;
23520  } else {
23521  cimg::swap((*this)(pos,0),(*this)(right,0)); cimg::swap((*this)(pos,1),(*this)(right,1));
23522  cimg::swap((*this)(pos,2),(*this)(right,2)); cimg::swap((*this)(pos,3),(*this)(right,3));
23523  pos = right;
23524  }
23525  } else {
23526  cimg::swap((*this)(pos,0),(*this)(left,0)); cimg::swap((*this)(pos,1),(*this)(left,1));
23527  cimg::swap((*this)(pos,2),(*this)(left,2)); cimg::swap((*this)(pos,3),(*this)(left,3));
23528  pos = left;
23529  }
23530  }
23531  return *this;
23532  }
23533 
23535 
23541  CImg<T>& deriche(const float sigma, const int order=0, const char axis='x', const bool boundary_conditions=true) {
23542 #define _cimg_deriche_apply \
23543  Tfloat *ptrY = Y._data, yb = 0, yp = 0; \
23544  T xp = (T)0; \
23545  if (boundary_conditions) { xp = *ptrX; yb = yp = (Tfloat)(coefp*xp); } \
23546  for (int m = 0; m<N; ++m) { \
23547  const T xc = *ptrX; ptrX+=off; \
23548  const Tfloat yc = *(ptrY++) = (Tfloat)(a0*xc + a1*xp - b1*yp - b2*yb); \
23549  xp = xc; yb = yp; yp = yc; \
23550  } \
23551  T xn = (T)0, xa = (T)0; \
23552  Tfloat yn = 0, ya = 0; \
23553  if (boundary_conditions) { xn = xa = *(ptrX-off); yn = ya = (Tfloat)coefn*xn; } \
23554  for (int n = N-1; n>=0; --n) { \
23555  const T xc = *(ptrX-=off); \
23556  const Tfloat yc = (Tfloat)(a2*xn + a3*xa - b1*yn - b2*ya); \
23557  xa = xn; xn = xc; ya = yn; yn = yc; \
23558  *ptrX = (T)(*(--ptrY)+yc); \
23559  }
23560  const char naxis = cimg::uncase(axis);
23561  const float nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
23562  if (is_empty() || (nsigma<0.1 && !order)) return *this;
23563  const float
23564  nnsigma = nsigma<0.1f?0.1f:nsigma,
23565  alpha = 1.695f/nnsigma,
23566  ema = (float)std::exp(-alpha),
23567  ema2 = (float)std::exp(-2*alpha),
23568  b1 = -2*ema,
23569  b2 = ema2;
23570  float a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
23571  switch (order) {
23572  case 0 : {
23573  const float k = (1-ema)*(1-ema)/(1+2*alpha*ema-ema2);
23574  a0 = k;
23575  a1 = k*(alpha-1)*ema;
23576  a2 = k*(alpha+1)*ema;
23577  a3 = -k*ema2;
23578  } break;
23579  case 1 : {
23580  const float k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema+1)*ema);
23581  a0 = a3 = 0;
23582  a1 = k*ema;
23583  a2 = -a1;
23584  } break;
23585  case 2 : {
23586  const float
23587  ea = (float)std::exp(-alpha),
23588  k = -(ema2-1)/(2*alpha*ema),
23589  kn = (-2*(-1+3*ea-3*ea*ea+ea*ea*ea)/(3*ea+1+3*ea*ea+ea*ea*ea));
23590  a0 = kn;
23591  a1 = -kn*(1+k*alpha)*ema;
23592  a2 = kn*(1-k*alpha)*ema;
23593  a3 = -kn*ema2;
23594  } break;
23595  default :
23596  throw CImgArgumentException(_cimg_instance
23597  "deriche(): Invalid specified filter order %u "
23598  "(should be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
23599  cimg_instance,
23600  order);
23601  }
23602  coefp = (a0+a1)/(1+b1+b2);
23603  coefn = (a2+a3)/(1+b1+b2);
23604  switch (naxis) {
23605  case 'x' : {
23606  const int N = _width;
23607  const unsigned long off = 1U;
23608  CImg<Tfloat> Y(N);
23609  cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
23610  } break;
23611  case 'y' : {
23612  const int N = _height;
23613  const unsigned long off = (unsigned long)_width;
23614  CImg<Tfloat> Y(N);
23615  cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
23616  } break;
23617  case 'z' : {
23618  const int N = _depth;
23619  const unsigned long off = (unsigned long)_width*_height;
23620  CImg<Tfloat> Y(N);
23621  cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
23622  } break;
23623  default : {
23624  const int N = _spectrum;
23625  const unsigned long off = (unsigned long)_width*_height*_depth;
23626  CImg<Tfloat> Y(N);
23627  cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
23628  }
23629  }
23630  return *this;
23631  }
23632 
23634  CImg<Tfloat> get_deriche(const float sigma, const int order=0, const char axis='x', const bool boundary_conditions=true) const {
23635  return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
23636  }
23637 
23639 
23648  CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z, const bool boundary_conditions=true) {
23649  if (!is_empty()) {
23650  if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
23651  if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
23652  if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
23653  }
23654  return *this;
23655  }
23656 
23658  CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z, const bool boundary_conditions=true) const {
23659  return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions);
23660  }
23661 
23663 
23667  CImg<T>& blur(const float sigma, const bool boundary_conditions=true) {
23668  const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
23669  return blur(nsigma,nsigma,nsigma,boundary_conditions);
23670  }
23671 
23673  CImg<Tfloat> get_blur(const float sigma, const bool boundary_conditions=true) const {
23674  return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions);
23675  }
23676 
23678 
23687  template<typename t>
23689  const float amplitude=60, const float dl=0.8f, const float da=30,
23690  const float gauss_prec=2, const unsigned int interpolation_type=0,
23691  const bool is_fast_approx=1) {
23692 
23693  // Check arguments and init variables
23694  if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
23695  throw CImgArgumentException(_cimg_instance
23696  "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
23697  cimg_instance,
23698  G._width,G._height,G._depth,G._spectrum,G._data);
23699 
23700  if (is_empty() || amplitude<=0 || dl<0) return *this;
23701  const bool is_3d = (G._spectrum==6);
23702  T val_min, val_max = max_min(val_min);
23703 
23704  if (da<=0) { // Iterated oriented Laplacians
23705  CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
23706  for (unsigned int iteration = 0; iteration<(unsigned int)amplitude; ++iteration) {
23707  Tfloat *ptrd = velocity._data, veloc_max = 0;
23708  if (is_3d) { // 3d version
23709  CImg_3x3x3(I,Tfloat);
23710  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
23711  const Tfloat
23712  ixx = Incc + Ipcc - 2*Iccc,
23713  ixy = (Innc + Ippc - Inpc - Ipnc)/4,
23714  ixz = (Incn + Ipcp - Incp - Ipcn)/4,
23715  iyy = Icnc + Icpc - 2*Iccc,
23716  iyz = (Icnn + Icpp - Icnp - Icpn)/4,
23717  izz = Iccn + Iccp - 2*Iccc,
23718  veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz + G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
23719  *(ptrd++) = veloc;
23720  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
23721  }
23722  } else { // 2d version
23723  CImg_3x3(I,Tfloat);
23724  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
23725  const Tfloat
23726  ixx = Inc + Ipc - 2*Icc,
23727  ixy = (Inn + Ipp - Inp - Ipn)/4,
23728  iyy = Icn + Icp - 2*Icc,
23729  veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
23730  *(ptrd++) = veloc;
23731  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
23732  }
23733  }
23734  if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
23735  }
23736  } else { // LIC-based smoothing.
23737  const unsigned long whd = (unsigned long)_width*_height*_depth;
23738  const float sqrt2amplitude = (float)std::sqrt(2*amplitude);
23739  const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
23740  CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum);
23741  int N = 0;
23742  if (is_3d) { // 3d version
23743  for (float phi = (180%(int)da)/2.0f; phi<=180; phi+=da) {
23744  const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)), da2 = datmp<1?360.0f:datmp;
23745  for (float theta = 0; theta<360; (theta+=da2),++N) {
23746  const float
23747  thetar = (float)(theta*cimg::PI/180),
23748  vx = (float)(std::cos(thetar)*std::cos(phir)),
23749  vy = (float)(std::sin(thetar)*std::cos(phir)),
23750  vz = (float)std::sin(phir);
23751  const t
23752  *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
23753  *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
23754  Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2), *pd3 = W.data(0,0,0,3);
23755  cimg_forXYZ(G,xg,yg,zg) {
23756  const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
23757  const float
23758  u = (float)(a*vx + b*vy + c*vz),
23759  v = (float)(b*vx + d*vy + e*vz),
23760  w = (float)(c*vx + e*vy + f*vz),
23761  n = (float)std::sqrt(1e-5+u*u+v*v+w*w),
23762  dln = dl/n;
23763  *(pd0++) = (Tfloat)(u*dln);
23764  *(pd1++) = (Tfloat)(v*dln);
23765  *(pd2++) = (Tfloat)(w*dln);
23766  *(pd3++) = (Tfloat)n;
23767  }
23768 
23769  Tfloat *ptrd = res._data;
23770  cimg_forXYZ(*this,x,y,z) {
23771  val.fill(0);
23772  const float
23773  n = (float)W(x,y,z,3),
23774  fsigma = (float)(n*sqrt2amplitude),
23775  fsigma2 = 2*fsigma*fsigma,
23776  length = gauss_prec*fsigma;
23777  float
23778  S = 0,
23779  X = (float)x,
23780  Y = (float)y,
23781  Z = (float)z;
23782  switch (interpolation_type) {
23783  case 0 : { // Nearest neighbor
23784  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
23785  const int
23786  cx = (int)(X+0.5f),
23787  cy = (int)(Y+0.5f),
23788  cz = (int)(Z+0.5f);
23789  const float
23790  u = (float)W(cx,cy,cz,0),
23791  v = (float)W(cx,cy,cz,1),
23792  w = (float)W(cx,cy,cz,2);
23793  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
23794  else {
23795  const float coef = (float)std::exp(-l*l/fsigma2);
23796  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
23797  S+=coef;
23798  }
23799  X+=u; Y+=v; Z+=w;
23800  }
23801  } break;
23802  case 1 : { // Linear interpolation
23803  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
23804  const float
23805  u = (float)(W._linear_atXYZ(X,Y,Z,0)),
23806  v = (float)(W._linear_atXYZ(X,Y,Z,1)),
23807  w = (float)(W._linear_atXYZ(X,Y,Z,2));
23808  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
23809  else {
23810  const float coef = (float)std::exp(-l*l/fsigma2);
23811  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
23812  S+=coef;
23813  }
23814  X+=u; Y+=v; Z+=w;
23815  }
23816  } break;
23817  default : { // 2nd order Runge Kutta
23818  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
23819  const float
23820  u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
23821  v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
23822  w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
23823  u = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,0)),
23824  v = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,1)),
23825  w = (float)(W._linear_atXYZ(X+u0,Y+v0,Z+w0,2));
23826  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
23827  else {
23828  const float coef = (float)std::exp(-l*l/fsigma2);
23829  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
23830  S+=coef;
23831  }
23832  X+=u; Y+=v; Z+=w;
23833  }
23834  } break;
23835  }
23836  Tfloat *_ptrd = ptrd++;
23837  if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; }
23838  else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,z,c)); _ptrd+=whd; }
23839  }
23840  }
23841  }
23842  } else { // 2d LIC algorithm
23843  for (float theta = (360%(int)da)/2.0f; theta<360; (theta+=da),++N) {
23844  const float thetar = (float)(theta*cimg::PI/180), vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
23845  const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
23846  Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
23847  cimg_forXY(G,xg,yg) {
23848  const t a = *(pa++), b = *(pb++), c = *(pc++);
23849  const float
23850  u = (float)(a*vx + b*vy),
23851  v = (float)(b*vx + c*vy),
23852  n = (float)std::sqrt(1e-5+u*u+v*v),
23853  dln = dl/n;
23854  *(pd0++) = (Tfloat)(u*dln);
23855  *(pd1++) = (Tfloat)(v*dln);
23856  *(pd2++) = (Tfloat)n;
23857  }
23858  Tfloat *ptrd = res._data;
23859  cimg_forXY(*this,x,y) {
23860  val.fill(0);
23861  const float
23862  n = (float)W(x,y,0,2),
23863  fsigma = (float)(n*sqrt2amplitude),
23864  fsigma2 = 2*fsigma*fsigma,
23865  length = gauss_prec*fsigma;
23866  float
23867  S = 0,
23868  X = (float)x,
23869  Y = (float)y;
23870  switch (interpolation_type) {
23871  case 0 : { // Nearest-neighbor
23872  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
23873  const int
23874  cx = (int)(X+0.5f),
23875  cy = (int)(Y+0.5f);
23876  const float
23877  u = (float)W(cx,cy,0,0),
23878  v = (float)W(cx,cy,0,1);
23879  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
23880  else {
23881  const float coef = (float)std::exp(-l*l/fsigma2);
23882  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
23883  S+=coef;
23884  }
23885  X+=u; Y+=v;
23886  }
23887  } break;
23888  case 1 : { // Linear interpolation
23889  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
23890  const float
23891  u = (float)(W._linear_atXY(X,Y,0,0)),
23892  v = (float)(W._linear_atXY(X,Y,0,1));
23893  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
23894  else {
23895  const float coef = (float)std::exp(-l*l/fsigma2);
23896  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
23897  S+=coef;
23898  }
23899  X+=u; Y+=v;
23900  }
23901  } break;
23902  default : { // 2nd-order Runge-kutta interpolation
23903  for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
23904  const float
23905  u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
23906  v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
23907  u = (float)(W._linear_atXY(X+u0,Y+v0,0,0)),
23908  v = (float)(W._linear_atXY(X+u0,Y+v0,0,1));
23909  if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
23910  else {
23911  const float coef = (float)std::exp(-l*l/fsigma2);
23912  cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
23913  S+=coef;
23914  }
23915  X+=u; Y+=v;
23916  }
23917  }
23918  }
23919  Tfloat *_ptrd = ptrd++;
23920  if (S>0) cimg_forC(res,c) { *_ptrd+=val[c]/S; _ptrd+=whd; }
23921  else cimg_forC(res,c) { *_ptrd+=(Tfloat)((*this)(x,y,0,c)); _ptrd+=whd; }
23922  }
23923  }
23924  }
23925  const Tfloat *ptrs = res._data;
23926  cimg_for(*this,ptrd,T) { const Tfloat val = *(ptrs++)/N; *ptrd = val<val_min?val_min:(val>val_max?val_max:(T)val); }
23927  }
23928  return *this;
23929  }
23930 
23932  template<typename t>
23934  const float amplitude=60, const float dl=0.8f, const float da=30,
23935  const float gauss_prec=2, const unsigned int interpolation_type=0,
23936  const bool is_fast_approx=true) const {
23937  return (+*this).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
23938  }
23939 
23941 
23953  CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
23954  const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
23955  const float gauss_prec=2, const unsigned int interpolation_type=0,
23956  const bool is_fast_approx=true) {
23957  return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,alpha,sigma,interpolation_type!=3),
23958  amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
23959  }
23960 
23962  CImg<T> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
23963  const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
23964  const float da=30, const float gauss_prec=2, const unsigned int interpolation_type=0,
23965  const bool is_fast_approx=true) const {
23966  return (+*this).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,interpolation_type,is_fast_approx);
23967  }
23968 
23970 
23983  CImg<T>& blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
23984  const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
23985  const bool interpolation_type=true) {
23986  if (is_empty()) return *this;
23987  T m, M = max_min(m);
23988  const float range = (float)(1.0f + M - m);
23989  const unsigned int
23990  bx0 = bgrid_x>=0?bgrid_x:_width*(-bgrid_x)/100,
23991  by0 = bgrid_y>=0?bgrid_y:_height*(-bgrid_y)/100,
23992  bz0 = bgrid_z>=0?bgrid_z:_depth*(-bgrid_z)/100,
23993  br0 = bgrid_r>=0?bgrid_r:(int)(-range*bgrid_r/100),
23994  bx = bx0>0?bx0:1,
23995  by = by0>0?by0:1,
23996  bz = bz0>0?bz0:1,
23997  br = br0>0?br0:1;
23998  const float
23999  _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
24000  _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
24001  _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
24002  nsigma_x = _sigma_x*bx/_width,
24003  nsigma_y = _sigma_y*by/_height,
24004  nsigma_z = _sigma_z*bz/_depth,
24005  nsigma_r = sigma_r*br/range;
24006  if (nsigma_x>0 || nsigma_y>0 || nsigma_z>0 || nsigma_r>0) {
24007  const bool is_3d = (_depth>1);
24008  if (is_3d) { // 3d version of the algorithm
24009  CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
24010  cimg_forC(*this,c) {
24011  bgrid.fill(0); bgridw.fill(0);
24012  cimg_forXYZ(*this,x,y,z) {
24013  const T val = (*this)(x,y,z,c);
24014  const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range);
24015  bgrid(X,Y,Z,R) += (float)val;
24016  bgridw(X,Y,Z,R) += 1;
24017  }
24018  bgrid.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
24019  bgridw.blur(nsigma_x,nsigma_y,nsigma_z,true).deriche(nsigma_r,0,'c',false);
24020  if (interpolation_type) cimg_forXYZ(*this,x,y,z) {
24021  const T val = (*this)(x,y,z,c);
24022  const float X = (float)x*bx/_width, Y = (float)y*by/_height, Z = (float)z*bz/_depth, R = (float)((val-m)*br/range),
24023  bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
24024  (*this)(x,y,z,c) = (T)(bval0/bval1);
24025  } else cimg_forXYZ(*this,x,y,z) {
24026  const T val = (*this)(x,y,z,c);
24027  const int X = x*bx/_width, Y = y*by/_height, Z = z*bz/_depth, R = (int)((val-m)*br/range);
24028  const float bval0 = bgrid(X,Y,Z,R), bval1 = bgridw(X,Y,Z,R);
24029  (*this)(x,y,z,c) = (T)(bval0/bval1);
24030  }
24031  }
24032  } else { // 2d version of the algorithm
24033  CImg<floatT> bgrid(bx,by,br,2);
24034  cimg_forC(*this,c) {
24035  bgrid.fill(0);
24036  cimg_forXY(*this,x,y) {
24037  const T val = (*this)(x,y,c);
24038  const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range);
24039  bgrid(X,Y,R,0) += (float)val;
24040  bgrid(X,Y,R,1) += 1;
24041  }
24042  bgrid.blur(nsigma_x,nsigma_y,0,true).blur(0,0,nsigma_r,false);
24043  if (interpolation_type) cimg_forXY(*this,x,y) {
24044  const T val = (*this)(x,y,c);
24045  const float X = (float)x*bx/_width, Y = (float)y*by/_height, R = (float)((val-m)*br/range),
24046  bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
24047  (*this)(x,y,c) = (T)(bval0/bval1);
24048  } else cimg_forXY(*this,x,y) {
24049  const T val = (*this)(x,y,c);
24050  const int X = x*bx/_width, Y = y*by/_height, R = (int)((val-m)*br/range);
24051  const float bval0 = bgrid(X,Y,R,0), bval1 = bgrid(X,Y,R,1);
24052  (*this)(x,y,c) = (T)(bval0/bval1);
24053  }
24054  }
24055  }
24056  }
24057  return *this;
24058  }
24059 
24061  CImg<T> get_blur_bilateral(const float sigma_x, const float sigma_y, const float sigma_z, const float sigma_r,
24062  const int bgrid_x, const int bgrid_y, const int bgrid_z, const int bgrid_r,
24063  const bool interpolation_type=true) const {
24064  return (+*this).blur_bilateral(sigma_x,sigma_y,sigma_z,sigma_r,bgrid_x,bgrid_y,bgrid_z,bgrid_r,interpolation_type);
24065  }
24066 
24068 
24075  CImg<T>& blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
24076  const bool interpolation_type=true) {
24077  const float nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
24078  return blur_bilateral(nsigma_s,nsigma_s,nsigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type);
24079  }
24080 
24082  CImg<T> get_blur_bilateral(const float sigma_s, const float sigma_r, const int bgrid_s=-33, const int bgrid_r=32,
24083  const bool interpolation_type=true) const {
24084  return (+*this).blur_bilateral(sigma_s,sigma_s,sigma_s,sigma_r,bgrid_s,bgrid_s,bgrid_s,bgrid_r,interpolation_type);
24085  }
24086 
24088 
24096  CImg<T>& blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
24097  const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
24098  if (is_empty() || !patch_size || !lookup_size) return *this;
24099  return get_blur_patch(sigma_s,sigma_p,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
24100  }
24101 
24103  CImg<T> get_blur_patch(const float sigma_s, const float sigma_p, const unsigned int patch_size=3,
24104  const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) const {
24105 
24106 #define _cimg_blur_patch3d_fast(N) \
24107  cimg_for##N##XYZ(res,x,y,z) { \
24108  T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
24109  const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
24110  float sum_weights = 0; \
24111  cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0) - img(p,q,r,0))<sigma_p3) { \
24112  T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
24113  float distance2 = 0; \
24114  pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
24115  distance2/=Pnorm; \
24116  const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
24117  alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
24118  sum_weights+=weight; \
24119  cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
24120  } \
24121  if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
24122  else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
24123  }
24124 
24125 #define _cimg_blur_patch3d(N) \
24126  cimg_for##N##XYZ(res,x,y,z) { \
24127  T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,x,y,z,c,pP,T); pP+=N3; } \
24128  const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
24129  float sum_weights = 0, weight_max = 0; \
24130  cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
24131  T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N##x##N(img,p,q,r,c,pQ,T); pQ+=N3; } \
24132  float distance2 = 0; \
24133  pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
24134  distance2/=Pnorm; \
24135  const float dx = (float)p - x, dy = (float)q - y, dz = (float)r - z, \
24136  alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = (float)std::exp(-alldist); \
24137  if (weight>weight_max) weight_max = weight; \
24138  sum_weights+=weight; \
24139  cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c); \
24140  } \
24141  sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c); \
24142  if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights; \
24143  else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
24144  }
24145 
24146 #define _cimg_blur_patch2d_fast(N) \
24147  cimg_for##N##XY(res,x,y) { \
24148  T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
24149  const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
24150  float sum_weights = 0; \
24151  cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0,0) - img(p,q,0,0))<sigma_p3) { \
24152  T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
24153  float distance2 = 0; \
24154  pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
24155  distance2/=Pnorm; \
24156  const float dx = (float)p - x, dy = (float)q - y, \
24157  alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0.0f:1.0f; \
24158  sum_weights+=weight; \
24159  cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
24160  } \
24161  if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
24162  else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
24163  }
24164 
24165 #define _cimg_blur_patch2d(N) \
24166  cimg_for##N##XY(res,x,y) { \
24167  T *pP = P._data; cimg_forC(res,c) { cimg_get##N##x##N(img,x,y,0,c,pP,T); pP+=N2; } \
24168  const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
24169  float sum_weights = 0, weight_max = 0; \
24170  cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
24171  T *pQ = Q._data; cimg_forC(res,c) { cimg_get##N##x##N(img,p,q,0,c,pQ,T); pQ+=N2; } \
24172  float distance2 = 0; \
24173  pQ = Q._data; cimg_for(P,pP,T) { const float dI = (float)*pP - (float)*(pQ++); distance2+=dI*dI; } \
24174  distance2/=Pnorm; \
24175  const float dx = (float)p - x, dy = (float)q - y, \
24176  alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = (float)std::exp(-alldist); \
24177  if (weight>weight_max) weight_max = weight; \
24178  sum_weights+=weight; \
24179  cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c); \
24180  } \
24181  sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c); \
24182  if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights; \
24183  else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
24184  }
24185 
24186  if (is_empty() || !patch_size || !lookup_size) return +*this;
24187  CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
24188  const CImg<T> _img = smoothness>0?get_blur(smoothness):CImg<Tfloat>(),&img = smoothness>0?_img:*this;
24189  CImg<T> P(patch_size*patch_size*_spectrum), Q(P);
24190  const float
24191  nsigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
24192  sigma_s2 = nsigma_s*nsigma_s, sigma_p2 = sigma_p*sigma_p, sigma_p3 = 3*sigma_p,
24193  Pnorm = P.size()*sigma_p2;
24194  const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
24195  const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
24196  if (_depth>1) switch (patch_size) { // 3d
24197  case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
24198  case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
24199  default : {
24200  const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
24201  if (is_fast_approx) cimg_forXYZ(res,x,y,z) { // Fast
24202  P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
24203  const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
24204  float sum_weights = 0;
24205  cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (cimg::abs(img(x,y,z,0)-img(p,q,r,0))<sigma_p3) {
24206  (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
24207  const float
24208  dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
24209  distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
24210  weight = distance2>3?0.0f:1.0f;
24211  sum_weights+=weight;
24212  cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
24213  }
24214  if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
24215  else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
24216  } else cimg_forXYZ(res,x,y,z) { // Exact
24217  P = img.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
24218  const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
24219  float sum_weights = 0, weight_max = 0;
24220  cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
24221  (Q = img.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
24222  const float
24223  dx = (float)x - p, dy = (float)y - q, dz = (float)z - r,
24224  distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
24225  weight = (float)std::exp(-distance2);
24226  if (weight>weight_max) weight_max = weight;
24227  sum_weights+=weight;
24228  cimg_forC(res,c) res(x,y,z,c)+=weight*(*this)(p,q,r,c);
24229  }
24230  sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=weight_max*(*this)(x,y,z,c);
24231  if (sum_weights>0) cimg_forC(res,c) res(x,y,z,c)/=sum_weights;
24232  else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
24233  }
24234  }
24235  } else switch (patch_size) { // 2d
24236  case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
24237  case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
24238  case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
24239  case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
24240  case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
24241  case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
24242  case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
24243  case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
24244  default : { // Fast
24245  const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
24246  if (is_fast_approx) cimg_forXY(res,x,y) { // 2d fast approximation.
24247  P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
24248  const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
24249  float sum_weights = 0;
24250  cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (cimg::abs(img(x,y,0)-img(p,q,0))<sigma_p3) {
24251  (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
24252  const float
24253  dx = (float)x - p, dy = (float)y - q,
24254  distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
24255  weight = distance2>3?0.0f:1.0f;
24256  sum_weights+=weight;
24257  cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
24258  }
24259  if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
24260  else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
24261  } else cimg_forXY(res,x,y) { // 2d exact algorithm.
24262  P = img.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
24263  const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
24264  float sum_weights = 0, weight_max = 0;
24265  cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
24266  (Q = img.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
24267  const float
24268  dx = (float)x - p, dy = (float)y - q,
24269  distance2 = (float)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
24270  weight = (float)std::exp(-distance2);
24271  if (weight>weight_max) weight_max = weight;
24272  sum_weights+=weight;
24273  cimg_forC(res,c) res(x,y,c)+=weight*(*this)(p,q,c);
24274  }
24275  sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=weight_max*(*this)(x,y,c);
24276  if (sum_weights>0) cimg_forC(res,c) res(x,y,c)/=sum_weights;
24277  else cimg_forC(res,c) res(x,y,0,c) = (Tfloat)((*this)(x,y,c));
24278  }
24279  }
24280  }
24281  return res;
24282  }
24283 
24285 
24288  CImg<T>& blur_median(const unsigned int n) {
24289  if (!n) return *this;
24290  return get_blur_median(n).move_to(*this);
24291  }
24292 
24294  CImg<T> get_blur_median(const unsigned int n) const {
24295  if (is_empty() || n<=1) return +*this;
24296  CImg<T> res(_width,_height,_depth,_spectrum);
24297  T *ptrd = res._data;
24298  const int hl = n/2, hr = hl - 1 + n%2;
24299  if (res._depth!=1) cimg_forXYZC(*this,x,y,z,c) { // 3d
24300  const int
24301  x0 = x - hl, y0 = y - hl, z0 = z-hl, x1 = x + hr, y1 = y + hr, z1 = z+hr,
24302  nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
24303  nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1, nz1 = z1>=depth()?depth()-1:z1;
24304  *(ptrd++) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
24305  } else {
24306 #define _cimg_median_sort(a,b) if ((a)>(b)) cimg::swap(a,b)
24307  if (res._height!=1) switch (n) { // 2d
24308  case 3 : {
24309  T I[9] = { 0 };
24310  CImg_3x3(J,T);
24311  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
24312  std::memcpy(J,I,9*sizeof(T));
24313  _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
24314  _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jpn, Jcn);
24315  _cimg_median_sort(Jcp, Jnp); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jcn, Jnn);
24316  _cimg_median_sort(Jpp, Jpc); _cimg_median_sort(Jnc, Jnn); _cimg_median_sort(Jcc, Jcn);
24317  _cimg_median_sort(Jpc, Jpn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jnp, Jnc);
24318  _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcc, Jnp); _cimg_median_sort(Jpn, Jcc);
24319  _cimg_median_sort(Jcc, Jnp);
24320  *(ptrd++) = Jcc;
24321  }
24322  } break;
24323  case 5 : {
24324  T I[25] = { 0 };
24325  CImg_5x5(J,T);
24326  cimg_forC(*this,c) cimg_for5x5(*this,x,y,0,c,I,T) {
24327  std::memcpy(J,I,25*sizeof(T));
24328  _cimg_median_sort(Jbb, Jpb); _cimg_median_sort(Jnb, Jab); _cimg_median_sort(Jcb, Jab); _cimg_median_sort(Jcb, Jnb);
24329  _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbp, Jcp); _cimg_median_sort(Jbp, Jpp); _cimg_median_sort(Jap, Jbc);
24330  _cimg_median_sort(Jnp, Jbc); _cimg_median_sort(Jnp, Jap); _cimg_median_sort(Jcc, Jnc); _cimg_median_sort(Jpc, Jnc);
24331  _cimg_median_sort(Jpc, Jcc); _cimg_median_sort(Jbn, Jpn); _cimg_median_sort(Jac, Jpn); _cimg_median_sort(Jac, Jbn);
24332  _cimg_median_sort(Jnn, Jan); _cimg_median_sort(Jcn, Jan); _cimg_median_sort(Jcn, Jnn); _cimg_median_sort(Jpa, Jca);
24333  _cimg_median_sort(Jba, Jca); _cimg_median_sort(Jba, Jpa); _cimg_median_sort(Jna, Jaa); _cimg_median_sort(Jcb, Jbp);
24334  _cimg_median_sort(Jnb, Jpp); _cimg_median_sort(Jbb, Jpp); _cimg_median_sort(Jbb, Jnb); _cimg_median_sort(Jab, Jcp);
24335  _cimg_median_sort(Jpb, Jcp); _cimg_median_sort(Jpb, Jab); _cimg_median_sort(Jpc, Jac); _cimg_median_sort(Jnp, Jac);
24336  _cimg_median_sort(Jnp, Jpc); _cimg_median_sort(Jcc, Jbn); _cimg_median_sort(Jap, Jbn); _cimg_median_sort(Jap, Jcc);
24337  _cimg_median_sort(Jnc, Jpn); _cimg_median_sort(Jbc, Jpn); _cimg_median_sort(Jbc, Jnc); _cimg_median_sort(Jba, Jna);
24338  _cimg_median_sort(Jcn, Jna); _cimg_median_sort(Jcn, Jba); _cimg_median_sort(Jpa, Jaa); _cimg_median_sort(Jnn, Jaa);
24339  _cimg_median_sort(Jnn, Jpa); _cimg_median_sort(Jan, Jca); _cimg_median_sort(Jnp, Jcn); _cimg_median_sort(Jap, Jnn);
24340  _cimg_median_sort(Jbb, Jnn); _cimg_median_sort(Jbb, Jap); _cimg_median_sort(Jbc, Jan); _cimg_median_sort(Jpb, Jan);
24341  _cimg_median_sort(Jpb, Jbc); _cimg_median_sort(Jpc, Jba); _cimg_median_sort(Jcb, Jba); _cimg_median_sort(Jcb, Jpc);
24342  _cimg_median_sort(Jcc, Jpa); _cimg_median_sort(Jnb, Jpa); _cimg_median_sort(Jnb, Jcc); _cimg_median_sort(Jnc, Jca);
24343  _cimg_median_sort(Jab, Jca); _cimg_median_sort(Jab, Jnc); _cimg_median_sort(Jac, Jna); _cimg_median_sort(Jbp, Jna);
24344  _cimg_median_sort(Jbp, Jac); _cimg_median_sort(Jbn, Jaa); _cimg_median_sort(Jpp, Jaa); _cimg_median_sort(Jpp, Jbn);
24345  _cimg_median_sort(Jcp, Jpn); _cimg_median_sort(Jcp, Jan); _cimg_median_sort(Jnc, Jpa); _cimg_median_sort(Jbn, Jna);
24346  _cimg_median_sort(Jcp, Jnc); _cimg_median_sort(Jcp, Jbn); _cimg_median_sort(Jpb, Jap); _cimg_median_sort(Jnb, Jpc);
24347  _cimg_median_sort(Jbp, Jcn); _cimg_median_sort(Jpc, Jcn); _cimg_median_sort(Jap, Jcn); _cimg_median_sort(Jab, Jbc);
24348  _cimg_median_sort(Jpp, Jcc); _cimg_median_sort(Jcp, Jac); _cimg_median_sort(Jab, Jpp); _cimg_median_sort(Jab, Jcp);
24349  _cimg_median_sort(Jcc, Jac); _cimg_median_sort(Jbc, Jac); _cimg_median_sort(Jpp, Jcp); _cimg_median_sort(Jbc, Jcc);
24350  _cimg_median_sort(Jpp, Jbc); _cimg_median_sort(Jpp, Jcn); _cimg_median_sort(Jcc, Jcn); _cimg_median_sort(Jcp, Jcn);
24351  _cimg_median_sort(Jcp, Jbc); _cimg_median_sort(Jcc, Jnn); _cimg_median_sort(Jcp, Jcc); _cimg_median_sort(Jbc, Jnn);
24352  _cimg_median_sort(Jcc, Jba); _cimg_median_sort(Jbc, Jba); _cimg_median_sort(Jbc, Jcc);
24353  *(ptrd++) = Jcc;
24354  }
24355  } break;
24356  default : {
24357  cimg_forXYC(*this,x,y,c) {
24358  const int
24359  x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
24360  nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
24361  nx1 = x1>=width()?width()-1:x1, ny1 = y1>=height()?height()-1:y1;
24362  *(ptrd++) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
24363  }
24364  }
24365  } else switch (n) { // 1d
24366  case 2 : {
24367  T I[4] = { 0 };
24368  cimg_forC(*this,c) cimg_for2x2(*this,x,y,0,c,I,T) *(ptrd++) = (T)(0.5f*(I[0]+I[1]));
24369  } break;
24370  case 3 : {
24371  T I[9] = { 0 };
24372  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T)
24373  *(ptrd++) = I[3]<I[4]?(I[4]<I[5]?I[4]:(I[3]<I[5]?I[5]:I[3])):(I[3]<I[5]?I[3]:(I[4]<I[5]?I[5]:I[4]));
24374  } break;
24375  default : {
24376  cimg_forXC(*this,x,c) {
24377  const int
24378  x0 = x - hl, x1 = x + hr,
24379  nx0 = x0<0?0:x0, nx1 = x1>=width()?width()-1:x1;
24380  *(ptrd++) = get_crop(nx0,0,0,c,nx1,0,0,c).median();
24381  }
24382  }
24383  }
24384  }
24385  return res;
24386  }
24387 
24389 
24396  CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) {
24397  if (is_empty()) return *this;
24398  T val_min, val_max = max_min(val_min);
24399  const float nedge = edge/2;
24400  CImg<Tfloat> val, vec, velocity(_width,_height,_depth,_spectrum);
24401  Tfloat *ptrd = velocity._data, veloc_max = 0;
24402 
24403  if (_depth>1) { // 3d
24404  CImg_3x3x3(I,Tfloat);
24405  if (sharpen_type) { // Shock filters.
24406  CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
24407  if (sigma>0) G.blur(sigma);
24408  Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2), *ptrG3 = G.data(0,0,0,3);
24409  cimg_forXYZ(G,x,y,z) {
24410  G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
24411  if (val[0]<0) val[0] = 0;
24412  if (val[1]<0) val[1] = 0;
24413  if (val[2]<0) val[2] = 0;
24414  *(ptrG0++) = vec(0,0);
24415  *(ptrG1++) = vec(0,1);
24416  *(ptrG2++) = vec(0,2);
24417  *(ptrG3++) = 1 - (Tfloat)std::pow(1+val[0]+val[1]+val[2],-(Tfloat)nedge);
24418  }
24419  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24420  const Tfloat
24421  u = G(x,y,z,0),
24422  v = G(x,y,z,1),
24423  w = G(x,y,z,2),
24424  amp = G(x,y,z,3),
24425  ixx = Incc + Ipcc - 2*Iccc,
24426  ixy = (Innc + Ippc - Inpc - Ipnc)/4,
24427  ixz = (Incn + Ipcp - Incp - Ipcn)/4,
24428  iyy = Icnc + Icpc - 2*Iccc,
24429  iyz = (Icnn + Icpp - Icnp - Icpn)/4,
24430  izz = Iccn + Iccp - 2*Iccc,
24431  ixf = Incc - Iccc,
24432  ixb = Iccc - Ipcc,
24433  iyf = Icnc - Iccc,
24434  iyb = Iccc - Icpc,
24435  izf = Iccn - Iccc,
24436  izb = Iccc - Iccp,
24437  itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
24438  it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
24439  veloc = -amp*cimg::sign(itt)*cimg::abs(it);
24440  *(ptrd++) = veloc;
24441  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24442  }
24443  } else cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) { // Inverse diffusion.
24444  const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
24445  *(ptrd++) = veloc;
24446  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24447  }
24448  } else {
24449  CImg_3x3(I,Tfloat);
24450  if (sharpen_type) { // Shock filters.
24451  CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
24452  if (sigma>0) G.blur(sigma);
24453  Tfloat *ptrG0 = G.data(0,0,0,0), *ptrG1 = G.data(0,0,0,1), *ptrG2 = G.data(0,0,0,2);
24454  cimg_forXY(G,x,y) {
24455  G.get_tensor_at(x,y).symmetric_eigen(val,vec);
24456  if (val[0]<0) val[0] = 0;
24457  if (val[1]<0) val[1] = 0;
24458  *(ptrG0++) = vec(0,0);
24459  *(ptrG1++) = vec(0,1);
24460  *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
24461  }
24462  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24463  const Tfloat
24464  u = G(x,y,0),
24465  v = G(x,y,1),
24466  amp = G(x,y,2),
24467  ixx = Inc + Ipc - 2*Icc,
24468  ixy = (Inn + Ipp - Inp - Ipn)/4,
24469  iyy = Icn + Icp - 2*Icc,
24470  ixf = Inc - Icc,
24471  ixb = Icc - Ipc,
24472  iyf = Icn - Icc,
24473  iyb = Icc - Icp,
24474  itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
24475  it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
24476  veloc = -amp*cimg::sign(itt)*cimg::abs(it);
24477  *(ptrd++) = veloc;
24478  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24479  }
24480  } else cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) { // Inverse diffusion.
24481  const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
24482  *(ptrd++) = veloc;
24483  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
24484  }
24485  }
24486  if (veloc_max<=0) return *this;
24487  return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
24488  }
24489 
24491  CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1, const float alpha=0, const float sigma=0) const {
24492  return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
24493  }
24494 
24496 
24506  CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=3) const {
24507  CImgList<Tfloat> grad(2,_width,_height,_depth,_spectrum);
24508  Tfloat *ptrd0 = grad[0]._data, *ptrd1 = grad[1]._data;
24509  bool is_3d = false;
24510  if (axes) {
24511  for (unsigned int a = 0; axes[a]; ++a) {
24512  const char axis = cimg::uncase(axes[a]);
24513  switch (axis) {
24514  case 'x' : case 'y' : break;
24515  case 'z' : is_3d = true; break;
24516  default :
24517  throw CImgArgumentException(_cimg_instance
24518  "get_gradient(): Invalid specified axis '%c'.",
24519  cimg_instance,
24520  axis);
24521  }
24522  }
24523  } else is_3d = (_depth>1);
24524  if (is_3d) {
24525  CImg<Tfloat>(_width,_height,_depth,_spectrum).move_to(grad);
24526  Tfloat *ptrd2 = grad[2]._data;
24527  switch (scheme) { // 3d.
24528  case -1 : { // Backward finite differences.
24529  CImg_3x3x3(I,Tfloat);
24530  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24531  *(ptrd0++) = Iccc - Ipcc;
24532  *(ptrd1++) = Iccc - Icpc;
24533  *(ptrd2++) = Iccc - Iccp;
24534  }
24535  } break;
24536  case 1 : { // Forward finite differences.
24537  CImg_2x2x2(I,Tfloat);
24538  cimg_forC(*this,c) cimg_for2x2x2(*this,x,y,z,c,I,Tfloat) {
24539  *(ptrd0++) = Incc - Iccc;
24540  *(ptrd1++) = Icnc - Iccc;
24541  *(ptrd2++) = Iccn - Iccc;
24542  }
24543  } break;
24544  case 4 : { // Using Deriche filter with low standard variation.
24545  grad[0] = get_deriche(0,1,'x');
24546  grad[1] = get_deriche(0,1,'y');
24547  grad[2] = get_deriche(0,1,'z');
24548  } break;
24549  default : { // Central finite differences.
24550  CImg_3x3x3(I,Tfloat);
24551  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24552  *(ptrd0++) = (Incc - Ipcc)/2;
24553  *(ptrd1++) = (Icnc - Icpc)/2;
24554  *(ptrd2++) = (Iccn - Iccp)/2;
24555  }
24556  }
24557  }
24558  } else switch (scheme) { // 2d.
24559  case -1 : { // Backward finite differences.
24560  CImg_3x3(I,Tfloat);
24561  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
24562  *(ptrd0++) = Icc - Ipc;
24563  *(ptrd1++) = Icc - Icp;
24564  }
24565  } break;
24566  case 1 : { // Forward finite differences.
24567  CImg_2x2(I,Tfloat);
24568  cimg_forZC(*this,z,c) cimg_for2x2(*this,x,y,z,c,I,Tfloat) {
24569  *(ptrd0++) = Inc - Icc;
24570  *(ptrd1++) = Icn - Icc;
24571  }
24572  } break;
24573  case 2 : { // Sobel scheme.
24574  CImg_3x3(I,Tfloat);
24575  const Tfloat a = 1, b = 2;
24576  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
24577  *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
24578  *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
24579  }
24580  } break;
24581  case 3 : { // Rotation invariant mask.
24582  CImg_3x3(I,Tfloat);
24583  const Tfloat a = (Tfloat)(0.25f*(2-std::sqrt(2.0f))), b = (Tfloat)(0.5f*(std::sqrt(2.0f)-1));
24584  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
24585  *(ptrd0++) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
24586  *(ptrd1++) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
24587  }
24588  } break;
24589  case 4 : { // using Deriche filter with low standard variation
24590  grad[0] = get_deriche(0,1,'x');
24591  grad[1] = get_deriche(0,1,'y');
24592  } break;
24593  default : { // central finite differences
24594  CImg_3x3(I,Tfloat);
24595  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
24596  *(ptrd0++) = (Inc - Ipc)/2;
24597  *(ptrd1++) = (Icn - Icp)/2;
24598  }
24599  }
24600  }
24601  if (!axes) return grad;
24602  CImgList<Tfloat> res;
24603  for (unsigned int l = 0; axes[l]; ++l) {
24604  const char axis = cimg::uncase(axes[l]);
24605  switch (axis) {
24606  case 'x' : res.insert(grad[0]); break;
24607  case 'y' : res.insert(grad[1]); break;
24608  case 'z' : res.insert(grad[2]); break;
24609  }
24610  }
24611  grad.assign();
24612  return res;
24613  }
24614 
24616 
24619  CImgList<Tfloat> get_hessian(const char *const axes=0) const {
24620  CImgList<Tfloat> res;
24621  const char *naxes = axes, *const def_axes2d = "xxxyyy", *const def_axes3d = "xxxyxzyyyzzz";
24622  if (!axes) naxes = _depth>1?def_axes3d:def_axes2d;
24623  const unsigned int lmax = std::strlen(naxes);
24624  if (lmax%2)
24625  throw CImgArgumentException(_cimg_instance
24626  "get_hessian(): Invalid specified axes '%s'.",
24627  cimg_instance,
24628  naxes);
24629 
24630  res.assign(lmax/2,_width,_height,_depth,_spectrum);
24631  if (!cimg::strcasecmp(naxes,def_axes3d)) { // 3d
24632  Tfloat
24633  *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data,
24634  *ptrd3 = res[3]._data, *ptrd4 = res[4]._data, *ptrd5 = res[5]._data;
24635  CImg_3x3x3(I,Tfloat);
24636  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24637  *(ptrd0++) = Ipcc + Incc - 2*Iccc; // Ixx
24638  *(ptrd1++) = (Ippc + Innc - Ipnc - Inpc)/4; // Ixy
24639  *(ptrd2++) = (Ipcp + Incn - Ipcn - Incp)/4; // Ixz
24640  *(ptrd3++) = Icpc + Icnc - 2*Iccc; // Iyy
24641  *(ptrd4++) = (Icpp + Icnn - Icpn - Icnp)/4; // Iyz
24642  *(ptrd5++) = Iccn + Iccp - 2*Iccc; // Izz
24643  }
24644  } else if (!cimg::strcasecmp(naxes,def_axes2d)) { // 2d
24645  Tfloat *ptrd0 = res[0]._data, *ptrd1 = res[1]._data, *ptrd2 = res[2]._data;
24646  CImg_3x3(I,Tfloat);
24647  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24648  *(ptrd0++) = Ipc + Inc - 2*Icc; // Ixx
24649  *(ptrd1++) = (Ipp + Inn - Ipn - Inp)/4; // Ixy
24650  *(ptrd2++) = Icp + Icn - 2*Icc; // Iyy
24651  }
24652  } else for (unsigned int l = 0; l<lmax; ) { // Version with custom axes.
24653  const unsigned int l2 = l/2;
24654  char axis1 = naxes[l++], axis2 = naxes[l++];
24655  if (axis1>axis2) cimg::swap(axis1,axis2);
24656  bool valid_axis = false;
24657  Tfloat *ptrd = res[l2]._data;
24658  if (axis1=='x' && axis2=='x') { // Ixx
24659  valid_axis = true; CImg_3x3(I,Tfloat);
24660  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Ipc + Inc - 2*Icc;
24661  }
24662  else if (axis1=='x' && axis2=='y') { // Ixy
24663  valid_axis = true; CImg_3x3(I,Tfloat);
24664  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipp + Inn - Ipn - Inp)/4;
24665  }
24666  else if (axis1=='x' && axis2=='z') { // Ixz
24667  valid_axis = true; CImg_3x3x3(I,Tfloat);
24668  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Ipcp + Incn - Ipcn - Incp)/4;
24669  }
24670  else if (axis1=='y' && axis2=='y') { // Iyy
24671  valid_axis = true; CImg_3x3(I,Tfloat);
24672  cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Icp + Icn - 2*Icc;
24673  }
24674  else if (axis1=='y' && axis2=='z') { // Iyz
24675  valid_axis = true; CImg_3x3x3(I,Tfloat);
24676  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = (Icpp + Icnn - Icpn - Icnp)/4;
24677  }
24678  else if (axis1=='z' && axis2=='z') { // Izz
24679  valid_axis = true; CImg_3x3x3(I,Tfloat);
24680  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Iccn + Iccp - 2*Iccc;
24681  }
24682  else if (!valid_axis)
24683  throw CImgArgumentException(_cimg_instance
24684  "get_hessian(): Invalid specified axes '%s'.",
24685  cimg_instance,
24686  naxes);
24687  }
24688  return res;
24689  }
24690 
24693  return get_laplacian().move_to(*this);
24694  }
24695 
24698  if (is_empty()) return CImg<Tfloat>();
24699  CImg<Tfloat> res(_width,_height,_depth,_spectrum);
24700  Tfloat *ptrd = res._data;
24701  if (_depth>1) { // 3d
24702  CImg_3x3x3(I,Tfloat);
24703  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat)
24704  *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
24705  } else if (_height>1) { // 2d
24706  CImg_3x3(I,Tfloat);
24707  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat)
24708  *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
24709  } else { // 1d
24710  CImg_3x3(I,Tfloat);
24711  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat)
24712  *(ptrd++) = Inc + Ipc - 2*Icc;
24713  }
24714  return res;
24715  }
24716 
24718 
24721  CImg<T>& structure_tensors(const unsigned int scheme=2) {
24722  return get_structure_tensors(scheme).move_to(*this);
24723  }
24724 
24726  CImg<Tfloat> get_structure_tensors(const unsigned int scheme=2) const {
24727  if (is_empty()) return *this;
24728  CImg<Tfloat> res;
24729  if (_depth>1) { // 3d
24730  res.assign(_width,_height,_depth,6,0);
24731  CImg_3x3x3(I,Tfloat);
24732  switch (scheme) {
24733  case 0 : { // classical central finite differences
24734  cimg_forC(*this,c) {
24735  Tfloat
24736  *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
24737  *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
24738  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24739  const Tfloat
24740  ix = (Incc - Ipcc)/2,
24741  iy = (Icnc - Icpc)/2,
24742  iz = (Iccn - Iccp)/2;
24743  *(ptrd0++)+=ix*ix;
24744  *(ptrd1++)+=ix*iy;
24745  *(ptrd2++)+=ix*iz;
24746  *(ptrd3++)+=iy*iy;
24747  *(ptrd4++)+=iy*iz;
24748  *(ptrd5++)+=iz*iz;
24749  }
24750  }
24751  } break;
24752  case 1 : { // Forward/backward finite differences (version 1).
24753  cimg_forC(*this,c) {
24754  Tfloat
24755  *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
24756  *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
24757  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24758  const Tfloat
24759  ixf = Incc - Iccc, ixb = Iccc - Ipcc,
24760  iyf = Icnc - Iccc, iyb = Iccc - Icpc,
24761  izf = Iccn - Iccc, izb = Iccc - Iccp;
24762  *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*ixf + ixb*ixb)/4;
24763  *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
24764  *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
24765  *(ptrd3++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4;
24766  *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
24767  *(ptrd5++)+=(izf*izf + izf*izb + izb*izf + izb*izb)/4;
24768  }
24769  }
24770  } break;
24771  default : { // Forward/backward finite differences (version 2).
24772  cimg_forC(*this,c) {
24773  Tfloat
24774  *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
24775  *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
24776  cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
24777  const Tfloat
24778  ixf = Incc - Iccc, ixb = Iccc - Ipcc,
24779  iyf = Icnc - Iccc, iyb = Iccc - Icpc,
24780  izf = Iccn - Iccc, izb = Iccc - Iccp;
24781  *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
24782  *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
24783  *(ptrd2++)+=(ixf*izf + ixf*izb + ixb*izf + ixb*izb)/4;
24784  *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
24785  *(ptrd4++)+=(iyf*izf + iyf*izb + iyb*izf + iyb*izb)/4;
24786  *(ptrd5++)+=(izf*izf + izb*izb)/2;
24787  }
24788  }
24789  } break;
24790  }
24791  } else { // 2d
24792  res.assign(_width,_height,_depth,3,0);
24793  CImg_3x3(I,Tfloat);
24794  switch (scheme) {
24795  case 0 : { // classical central finite differences
24796  cimg_forC(*this,c) {
24797  Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
24798  cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24799  const Tfloat
24800  ix = (Inc - Ipc)/2,
24801  iy = (Icn - Icp)/2;
24802  *(ptrd0++)+=ix*ix;
24803  *(ptrd1++)+=ix*iy;
24804  *(ptrd2++)+=iy*iy;
24805  }
24806  }
24807  } break;
24808  case 1 : { // Forward/backward finite differences (version 1).
24809  cimg_forC(*this,c) {
24810  Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
24811  cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24812  const Tfloat
24813  ixf = Inc - Icc, ixb = Icc - Ipc,
24814  iyf = Icn - Icc, iyb = Icc - Icp;
24815  *(ptrd0++)+=(ixf*ixf + ixf*ixb + ixb*iyf + ixb*ixb)/4;
24816  *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
24817  *(ptrd2++)+=(iyf*iyf + iyf*iyb + iyb*iyf + iyb*iyb)/4;
24818  }
24819  }
24820  } break;
24821  default : { // Forward/backward finite differences (version 2).
24822  cimg_forC(*this,c) {
24823  Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
24824  cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
24825  const Tfloat
24826  ixf = Inc - Icc, ixb = Icc - Ipc,
24827  iyf = Icn - Icc, iyb = Icc - Icp;
24828  *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
24829  *(ptrd1++)+=(ixf*iyf + ixf*iyb + ixb*iyf + ixb*iyb)/4;
24830  *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
24831  }
24832  }
24833  } break;
24834  }
24835  }
24836  return res;
24837  }
24838 
24840 
24847  CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
24848  const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
24849  CImg<Tfloat> res;
24850  const float nsharpness = cimg::max(sharpness,1e-5f), power1 = (is_sqrt?0.5f:1)*nsharpness, power2 = power1/(1e-7f+1-anisotropy);
24851  blur(alpha).normalize(0,(T)255);
24852 
24853  if (_depth>1) { // 3d
24854  CImg<floatT> val(3), vec(3,3);
24855  get_structure_tensors().move_to(res).blur(sigma);
24856  Tfloat
24857  *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
24858  *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
24859  cimg_forXYZ(*this,x,y,z) {
24860  res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
24861  const float
24862  _l1 = val[2], _l2 = val[1], _l3 = val[0],
24863  l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
24864  ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
24865  vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
24866  wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
24867  n1 = (float)std::pow(1+l1+l2+l3,-power1),
24868  n2 = (float)std::pow(1+l1+l2+l3,-power2);
24869  *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
24870  *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
24871  *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
24872  *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
24873  *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
24874  *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
24875  }
24876  } else { // for 2d images
24877  CImg<floatT> val(2), vec(2,2);
24878  get_structure_tensors().move_to(res).blur(sigma);
24879  Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
24880  cimg_forXY(*this,x,y) {
24881  res.get_tensor_at(x,y).symmetric_eigen(val,vec);
24882  const float
24883  _l1 = val[1], _l2 = val[0],
24884  l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
24885  ux = vec(1,0), uy = vec(1,1),
24886  vx = vec(0,0), vy = vec(0,1),
24887  n1 = (float)std::pow(1+l1+l2,-power1),
24888  n2 = (float)std::pow(1+l1+l2,-power2);
24889  *(ptrd0++) = n1*ux*ux + n2*vx*vx;
24890  *(ptrd1++) = n1*ux*uy + n2*vx*vy;
24891  *(ptrd2++) = n1*uy*uy + n2*vy*vy;
24892  }
24893  }
24894  return res.move_to(*this);
24895  }
24896 
24898  CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
24899  const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
24900  return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
24901  }
24902 
24904 
24912  CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.0f,
24913  const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
24914  const bool is_backward=false) {
24915  return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward).move_to(*this);
24916  }
24917 
24920  const float smoothness=0.1f, const float precision=5.0f,
24921  const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
24922  const bool is_backward=false) const {
24923  if (is_empty() || !source) return +*this;
24924  if (!is_sameXYZC(source))
24925  throw CImgArgumentException(_cimg_instance
24926  "displacement(): Instance and source image (%u,%u,%u,%u,%p) have different dimensions.",
24927  cimg_instance,
24928  source._width,source._height,source._depth,source._spectrum,source._data);
24929  if (precision<0)
24930  throw CImgArgumentException(_cimg_instance
24931  "displacement(): Invalid specified precision %g "
24932  "(should be >=0)",
24933  cimg_instance,
24934  precision);
24935  const unsigned int _nb_scales = nb_scales>0?nb_scales:(unsigned int)(2*std::log((double)(cimg::max(_width,_height))));
24936  const float _precision = (float)std::pow(10.0,-(double)precision);
24937  float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
24938  const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
24939  const bool is_3d = source._depth>1;
24940  CImg<floatT> U;
24941 
24942  for (int scale = _nb_scales-1; scale>=0; --scale) {
24943  const float factor = (float)std::pow(1.5,(double)scale);
24944  const unsigned int
24945  _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
24946  _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
24947  _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
24948  if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // skip too small scales.
24949  const CImg<Tfloat>
24950  I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
24951  I2 = (get_resize(I1,2)-=tm)/=tdelta;
24952  if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
24953  else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
24954  float dt = 2, energy = cimg::type<float>::max();
24955  const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
24956 
24957  for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
24958  float _energy = 0;
24959  if (is_3d) { // 3d version.
24960  if (smoothness>=0) cimg_for3XYZ(U,x,y,z) { // Isotropic regularization.
24961  const float
24962  X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
24963  Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
24964  Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
24965  float delta_I = 0, _energy_regul = 0;
24966  if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
24967  else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c));
24968  cimg_forC(U,c) {
24969  const float
24970  Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
24971  Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
24972  Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
24973  Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
24974  Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
24975  Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
24976  U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c].linear_atXYZ(X,Y,Z) + smoothness* ( Uxx + Uyy + Uzz)))/(1+6*smoothness*dt);
24977  _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
24978  }
24979  _energy+=delta_I*delta_I + smoothness*_energy_regul;
24980  } else {
24981  const float nsmoothness = -smoothness;
24982  cimg_for3XYZ(U,x,y,z) { // Anisotropic regularization.
24983  const float
24984  X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
24985  Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
24986  Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
24987  float delta_I = 0, _energy_regul = 0;
24988  if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
24989  else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2.linear_atXYZ(X,Y,Z,c));
24990  cimg_forC(U,c) {
24991  const float
24992  Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
24993  Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
24994  Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
24995  N2 = Ux*Ux + Uy*Uy + Uz*Uz,
24996  N = std::sqrt(N2),
24997  N3 = 1e-5 + N2*N,
24998  coef_a = (1 - Ux*Ux/N2)/N,
24999  coef_b = -2*Ux*Uy/N3,
25000  coef_c = -2*Ux*Uz/N3,
25001  coef_d = (1 - Uy*Uy/N2)/N,
25002  coef_e = -2*Uy*Uz/N3,
25003  coef_f = (1 - Uz*Uz/N2)/N,
25004  Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
25005  Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
25006  Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
25007  Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
25008  Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
25009  Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
25010  U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c].linear_atXYZ(X,Y,Z) +
25011  nsmoothness* ( coef_a*Uxx + coef_b*Uxy + coef_c*Uxz + coef_d*Uyy + coef_e*Uyz + coef_f*Uzz ))
25012  )/(1+2*(coef_a+coef_d+coef_f)*nsmoothness*dt);
25013  _energy_regul+=N;
25014  }
25015  _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
25016  }
25017  }
25018  } else { // 2d version.
25019  if (smoothness>=0) cimg_for3XY(U,x,y) { // Isotropic regularization.
25020  const float
25021  X = is_backward?x - U(x,y,0):x + U(x,y,0),
25022  Y = is_backward?y - U(x,y,1):y + U(x,y,1);
25023  float delta_I = 0, _energy_regul = 0;
25024  if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c));
25025  else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c));
25026  cimg_forC(U,c) {
25027  const float
25028  Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
25029  Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
25030  Uxx = U(_n1x,y,c) + U(_p1x,y,c),
25031  Uyy = U(x,_n1y,c) + U(x,_p1y,c);
25032  U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c].linear_atXY(X,Y) + smoothness*( Uxx + Uyy )))/(1+4*smoothness*dt);
25033  _energy_regul+=Ux*Ux + Uy*Uy;
25034  }
25035  _energy+=delta_I*delta_I + smoothness*_energy_regul;
25036  } else {
25037  const float nsmoothness = -smoothness;
25038  cimg_for3XY(U,x,y) { // Anisotropic regularization.
25039  const float
25040  X = is_backward?x - U(x,y,0):x + U(x,y,0),
25041  Y = is_backward?y - U(x,y,1):y + U(x,y,1);
25042  float delta_I = 0, _energy_regul = 0;
25043  if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1.linear_atXY(X,Y,c) - I2(x,y,c));
25044  else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2.linear_atXY(X,Y,c));
25045  cimg_forC(U,c) {
25046  const float
25047  Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
25048  Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
25049  N2 = Ux*Ux + Uy*Uy,
25050  N = std::sqrt(N2),
25051  N3 = 1e-5 + N2*N,
25052  coef_a = Uy*Uy/N3,
25053  coef_b = -2*Ux*Uy/N3,
25054  coef_c = Ux*Ux/N3,
25055  Uxx = U(_n1x,y,c) + U(_p1x,y,c),
25056  Uyy = U(x,_n1y,c) + U(x,_p1y,c),
25057  Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
25058  U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c].linear_atXY(X,Y) + nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/(1+2*(coef_a+coef_c)*nsmoothness*dt);
25059  _energy_regul+=N;
25060  }
25061  _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
25062  }
25063  }
25064  }
25065  const float d_energy = (_energy - energy)/(sw*sh*sd);
25066  if (d_energy<=0 && -d_energy<_precision) break;
25067  if (d_energy>0) dt*=0.5f;
25068  energy = _energy;
25069  }
25070  }
25071  return U;
25072  }
25073 
25075 
25086  CImg<T>& distance(const T value, const unsigned int metric=2) {
25087  if (is_empty()) return *this;
25088  bool is_value = false;
25089  cimg_for(*this,ptr,T) *ptr = (T)(*ptr==value?is_value=true,0:999999999);
25090  if (!is_value) return fill(cimg::type<T>::max());
25091  switch (metric) {
25092  case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev.
25093  case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan.
25094  case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Euclidean.
25095  default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Squared Euclidean.
25096  }
25097  return *this;
25098  }
25099 
25101  CImg<Tfloat> get_distance(const T value, const unsigned int metric=2) const {
25102  return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
25103  }
25104 
25105  static long _distance_sep_edt(const long i, const long u, const long *const g) {
25106  return (u*u-i*i+g[u]-g[i])/(2*(u-i));
25107  }
25108 
25109  static long _distance_dist_edt(const long x, const long i, const long *const g) {
25110  return (x-i)*(x-i) + g[i];
25111  }
25112 
25113  static long _distance_sep_mdt(const long i, const long u, const long *const g) {
25114  return (u-i<=g[u]-g[i]?999999999:(g[u]-g[i]+u+i)/2);
25115  }
25116 
25117  static long _distance_dist_mdt(const long x, const long i, const long *const g) {
25118  return (x<i?i-x:x-i) + g[i];
25119  }
25120 
25121  static long _distance_sep_cdt(const long i, const long u, const long *const g) {
25122  const long h = (i+u)/2;
25123  if (g[i]<=g[u]) { return h<i+g[u]?i+g[u]:h; }
25124  return h<u-g[i]?h:u-g[i];
25125  }
25126 
25127  static long _distance_dist_cdt(const long x, const long i, const long *const g) {
25128  const long d = x<i?i-x:x-i;
25129  return d<g[i]?g[i]:d;
25130  }
25131 
25132  static void _distance_scan(const unsigned int len,
25133  const long *const g,
25134  long (*const sep)(const long, const long, const long *const),
25135  long (*const f)(const long, const long, const long *const),
25136  long *const s,
25137  long *const t,
25138  long *const dt) {
25139  long q = s[0] = t[0] = 0;
25140  for (int u = 1; u<(int)len; ++u) { // Forward scan.
25141  while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
25142  if (q<0) { q = 0; s[0] = u; }
25143  else { const long w = 1 + sep(s[q], u, g); if (w<(long)len) { ++q; s[q] = u; t[q] = w; }}
25144  }
25145  for (int u = (int)len-1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan.
25146  }
25147 
25148  CImg<T>& _distance_core(long (*const sep)(const long, const long, const long *const),
25149  long (*const f)(const long, const long, const long *const)) {
25150  const unsigned long wh = (unsigned long)_width*_height;
25151  cimg_forC(*this,c) {
25152  CImg<longT> g(_width), dt(_width), s(_width), t(_width);
25153  CImg<T> img = get_shared_channel(c);
25154  cimg_forYZ(*this,y,z) { // Over X-direction.
25155  cimg_forX(*this,x) g[x] = (long)img(x,y,z,0,wh);
25156  _distance_scan(_width,g,sep,f,s,t,dt);
25157  cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
25158  }
25159  g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
25160  cimg_forXZ(*this,x,z) { // Over Y-direction.
25161  cimg_forY(*this,y) g[y] = (long)img(x,y,z,0,wh);
25162  _distance_scan(_height,g,sep,f,s,t,dt);
25163  cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
25164  }
25165  if (_depth>1) {
25166  g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
25167  cimg_forXY(*this,x,y) { // Over Z-direction.
25168  cimg_forZ(*this,z) g[z] = (long)img(x,y,z,0,wh);
25169  _distance_scan(_depth,g,sep,f,s,t,dt);
25170  cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
25171  }
25172  }
25173  }
25174  return *this;
25175  }
25176 
25178 
25183  template<typename t>
25184  CImg<T>& distance(const T value, const CImg<t>& metric_mask) {
25185  if (is_empty()) return *this;
25186  bool is_value = false;
25187  cimg_for(*this,ptr,T) *ptr = (T)(*ptr==value?is_value=true,0:999999999);
25188  if (!is_value) return fill(cimg::type<T>::max());
25189  const unsigned long wh = (unsigned long)_width*_height;
25190  cimg_forC(*this,c) {
25191  CImg<T> img = get_shared_channel(c);
25192  cimg_forXYZ(metric_mask,dx,dy,dz) {
25193  const t weight = metric_mask(dx,dy,dz);
25194  if (weight) {
25195  for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan.
25196  for (int y = dy , ny = 0; y<height(); ++y,++ny) {
25197  for (int x = dx, nx = 0; x<width(); ++x,++nx) {
25198  const T dd = img(nx,ny,nz,0,wh) + weight;
25199  if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
25200  }
25201  }
25202  }
25203  for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan.
25204  for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
25205  for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
25206  const T dd = img(nx,ny,nz,0,wh) + weight;
25207  if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
25208  }
25209  }
25210  }
25211  }
25212  }
25213  }
25214  return *this;
25215  }
25216 
25218  template<typename t>
25219  CImg<Tfloat> get_distance(const T value, const CImg<t>& metric_mask) const {
25220  return CImg<Tfloat>(*this,false).distance(value,metric_mask);
25221  }
25222 
25224 
25230  CImg<T>& distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
25231  return get_distance_dijkstra(x,y,z).move_to(*this);
25232  }
25233 
25235  CImg<Tfloat> get_distance_dijkstra(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
25236  if (is_empty()) return *this;
25237  if (!containsXYZC(x,y,z,0))
25238  throw CImgArgumentException(_cimg_instance
25239  "distance_dijkstra(): image instance does not contain specified starting point (%u,%u,%u).",
25240  cimg_instance,
25241  x,y,z);
25242  if (_spectrum!=1)
25243  throw CImgInstanceException(_cimg_instance
25244  "distance_dijkstra(): image instance is not a scalar image.",
25245  cimg_instance);
25246  CImg<Tfloat> res(_width,_height,_depth,2);
25247  CImg<boolT> in_queue(_width,_height,_depth,1,0);
25248  CImg<Tint> Q;
25249  unsigned int sizeQ = 0;
25250 
25251  // Put specified point in priority queue.
25252  Q._priority_queue_insert(in_queue,sizeQ,0,x,y,z);
25253  res(x,y,z) = 0; res(x,y,z,1) = 0;
25254 
25255  // Start distance propagation.
25256  while (sizeQ) {
25257 
25258  // Get and remove point with minimal potential from the queue.
25259  const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
25260  const Tfloat potential = (Tfloat)-Q(0,0);
25261  Q._priority_queue_remove(sizeQ);
25262 
25263  // Update neighbors.
25264  Tfloat npot = 0;
25265  if (x-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x-1,y,z)+potential),x-1,y,z)) {
25266  res(x-1,y,z) = npot; res(x-1,y,z,1) = 2;
25267  }
25268  if (x+1<width() && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x+1,y,z)+potential),x+1,y,z)) {
25269  res(x+1,y,z) = npot; res(x+1,y,z,1) = 1;
25270  }
25271  if (y-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y-1,z)+potential),x,y-1,z)) {
25272  res(x,y-1,z) = npot; res(x,y-1,z,1) = 4;
25273  }
25274  if (y+1<height() && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y+1,z)+potential),x,y+1,z)) {
25275  res(x,y+1,z) = npot; res(x,y+1,z,1) = 3;
25276  }
25277  if (z-1>=0 && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y,z-1)+potential),x,y,z-1)) {
25278  res(x,y,z-1) = npot; res(x,y,z-1,1) = 6;
25279  }
25280  if (z+1<depth() && Q._priority_queue_insert(in_queue,sizeQ,-(npot=(*this)(x,y,z+1)+potential),x,y,z+1)) {
25281  res(x,y,z+1) = npot; res(x,y,z+1,1) = 5;
25282  }
25283  }
25284  return res;
25285  }
25286 
25288 
25293  CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
25294  if (is_empty()) return *this;
25295  CImg<Tfloat> velocity(*this);
25296  for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
25297  Tfloat *ptrd = velocity._data, veloc_max = 0;
25298  if (_depth>1) { // 3d
25299  CImg_3x3x3(I,Tfloat);
25300  cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
25301  const Tfloat
25302  gx = (Incc - Ipcc)/2,
25303  gy = (Icnc - Icpc)/2,
25304  gz = (Iccn - Iccp)/2,
25305  sgn = -cimg::sign(Iccc),
25306  ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
25307  iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
25308  iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
25309  ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy + gz*gz)),
25310  ngx = gx/ng,
25311  ngy = gy/ng,
25312  ngz = gz/ng,
25313  veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
25314  *(ptrd++) = veloc;
25315  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
25316  } else *(ptrd++) = 0;
25317  } else { // 2d version
25318  CImg_3x3(I,Tfloat);
25319  cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
25320  const Tfloat
25321  gx = (Inc - Ipc)/2,
25322  gy = (Icn - Icp)/2,
25323  sgn = -cimg::sign(Icc),
25324  ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
25325  iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
25326  ng = (Tfloat)(1e-5f + std::sqrt(gx*gx + gy*gy)),
25327  ngx = gx/ng,
25328  ngy = gy/ng,
25329  veloc = sgn*(ngx*ix + ngy*iy - 1);
25330  *(ptrd++) = veloc;
25331  if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
25332  } else *(ptrd++) = 0;
25333  }
25334  if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
25335  }
25336  return *this;
25337  }
25338 
25340  CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) const {
25341  return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
25342  }
25343 
25345 
25350  CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
25351  return get_haar(axis,invert,nb_scales).move_to(*this);
25352  }
25353 
25355  CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
25356  if (is_empty() || !nb_scales) return +*this;
25357  CImg<Tfloat> res;
25358  const Tfloat sqrt2 = std::sqrt(2);
25359  if (nb_scales==1) {
25360  switch (cimg::uncase(axis)) { // Single scale transform
25361  case 'x' : {
25362  const unsigned int w = _width/2;
25363  if (w) {
25364  if (w%2)
25365  throw CImgInstanceException(_cimg_instance
25366  "haar(): Sub-image width %u is not even at scale %u.",
25367  cimg_instance,
25368  w);
25369 
25370  res.assign(_width,_height,_depth,_spectrum);
25371  if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
25372  for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
25373  const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
25374  res(x2++,y,z,c) = val0 - val1;
25375  res(x2++,y,z,c) = val0 + val1;
25376  }
25377  } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
25378  for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
25379  const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
25380  res(x,y,z,c) = (val0 + val1)/2;
25381  res(xw,y,z,c) = (val1 - val0)/2;
25382  }
25383  }
25384  } else return *this;
25385  } break;
25386  case 'y' : {
25387  const unsigned int h = _height/2;
25388  if (h) {
25389  if (h%2)
25390  throw CImgInstanceException(_cimg_instance
25391  "haar(): Sub-image height %u is not even at scale %u.",
25392  cimg_instance,
25393  h);
25394 
25395  res.assign(_width,_height,_depth,_spectrum);
25396  if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
25397  for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
25398  const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
25399  res(x,y2++,z,c) = (val0 - val1)/sqrt2;
25400  res(x,y2++,z,c) = (val0 + val1)/sqrt2;
25401  }
25402  } else cimg_forXZC(*this,x,z,c) {
25403  for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
25404  const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
25405  res(x,y,z,c) = (val0 + val1)/sqrt2;
25406  res(x,yh,z,c) = (val1 - val0)/sqrt2;
25407  }
25408  }
25409  } else return *this;
25410  } break;
25411  case 'z' : {
25412  const unsigned int d = _depth/2;
25413  if (d) {
25414  if (d%2)
25415  throw CImgInstanceException(_cimg_instance
25416  "haar(): Sub-image depth %u is not even at scale %u.",
25417  cimg_instance,
25418  d);
25419 
25420  res.assign(_width,_height,_depth,_spectrum);
25421  if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
25422  for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
25423  const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
25424  res(x,y,z2++,c) = (val0 - val1)/sqrt2;
25425  res(x,y,z2++,c) = (val0 + val1)/sqrt2;
25426  }
25427  } else cimg_forXYC(*this,x,y,c) {
25428  for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
25429  const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
25430  res(x,y,z,c) = (val0 + val1)/sqrt2;
25431  res(x,y,zd,c) = (val1 - val0)/sqrt2;
25432  }
25433  }
25434  } else return *this;
25435  } break;
25436  default :
25437  throw CImgArgumentException(_cimg_instance
25438  "haar(): Invalid specified axis '%c' "
25439  "(should be { x | y | z }).",
25440  cimg_instance,
25441  axis);
25442  }
25443  } else { // Multi-scale version
25444  if (invert) {
25445  res.assign(*this);
25446  switch (cimg::uncase(axis)) {
25447  case 'x' : {
25448  unsigned int w = _width;
25449  for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
25450  for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',true,1));
25451  } break;
25452  case 'y' : {
25453  unsigned int h = _width;
25454  for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
25455  for (h = h?h:1; h<=_height; h*=2) res.draw_image(res.get_crop(0,0,_width-1,h-1).get_haar('y',true,1));
25456  } break;
25457  case 'z' : {
25458  unsigned int d = _depth;
25459  for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
25460  for (d = d?d:1; d<=_depth; d*=2) res.draw_image(res.get_crop(0,0,0,_width-1,_height-1,d-1).get_haar('z',true,1));
25461  } break;
25462  default :
25463  throw CImgArgumentException(_cimg_instance
25464  "haar(): Invalid specified axis '%c' "
25465  "(should be { x | y | z }).",
25466  cimg_instance,
25467  axis);
25468  }
25469  } else { // Direct transform
25470  res = get_haar(axis,false,1);
25471  switch (cimg::uncase(axis)) {
25472  case 'x' : {
25473  for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2) res.draw_image(res.get_crop(0,w-1).get_haar('x',false,1));
25474  } break;
25475  case 'y' : {
25476  for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2) res.draw_image(res.get_crop(0,0,_width-1,h-1).get_haar('y',false,1));
25477  } break;
25478  case 'z' : {
25479  for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2) res.draw_image(res.get_crop(0,0,0,_width-1,_height-1,d-1).get_haar('z',false,1));
25480  } break;
25481  default :
25482  throw CImgArgumentException(_cimg_instance
25483  "haar(): Invalid specified axis '%c' "
25484  "(should be { x | y | z }).",
25485  cimg_instance,
25486  axis);
25487  }
25488  }
25489  }
25490  return res;
25491  }
25492 
25494 
25498  CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
25499  return get_haar(invert,nb_scales).move_to(*this);
25500  }
25501 
25503  CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
25504  CImg<Tfloat> res;
25505 
25506  if (nb_scales==1) { // Single scale transform
25507  if (_width>1) get_haar('x',invert,1).move_to(res);
25508  if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
25509  if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
25510  if (res) return res;
25511  } else { // Multi-scale transform
25512  if (invert) { // Inverse transform
25513  res.assign(*this);
25514  if (_width>1) {
25515  if (_height>1) {
25516  if (_depth>1) {
25517  unsigned int w = _width, h = _height, d = _depth; for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
25518  for (w = w?w:1, h = h?h:1, d = d?d:1; w<=_width && h<=_height && d<=_depth; w*=2, h*=2, d*=2)
25519  res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).get_haar(true,1));
25520  } else {
25521  unsigned int w = _width, h = _height; for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
25522  for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
25523  res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).get_haar(true,1));
25524  }
25525  } else {
25526  if (_depth>1) {
25527  unsigned int w = _width, d = _depth; for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
25528  for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
25529  res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).get_haar(true,1));
25530  } else {
25531  unsigned int w = _width; for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
25532  for (w = w?w:1; w<=_width; w*=2)
25533  res.draw_image(res.get_crop(0,0,0,w-1,0,0).get_haar(true,1));
25534  }
25535  }
25536  } else {
25537  if (_height>1) {
25538  if (_depth>1) {
25539  unsigned int h = _height, d = _depth; for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
25540  for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
25541  res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).get_haar(true,1));
25542  } else {
25543  unsigned int h = _height; for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
25544  for (h = h?h:1; h<=_height; h*=2)
25545  res.draw_image(res.get_crop(0,0,0,0,h-1,0).get_haar(true,1));
25546  }
25547  } else {
25548  if (_depth>1) {
25549  unsigned int d = _depth; for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
25550  for (d = d?d:1; d<=_depth; d*=2)
25551  res.draw_image(res.get_crop(0,0,0,0,0,d-1).get_haar(true,1));
25552  } else return *this;
25553  }
25554  }
25555  } else { // Direct transform
25556  res = get_haar(false,1);
25557  if (_width>1) {
25558  if (_height>1) {
25559  if (_depth>1) for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales; ++s, w/=2, h/=2, d/=2)
25560  res.draw_image(res.get_crop(0,0,0,w-1,h-1,d-1).haar(false,1));
25561  else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
25562  res.draw_image(res.get_crop(0,0,0,w-1,h-1,0).haar(false,1));
25563  } else {
25564  if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
25565  res.draw_image(res.get_crop(0,0,0,w-1,0,d-1).haar(false,1));
25566  else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
25567  res.draw_image(res.get_crop(0,0,0,w-1,0,0).haar(false,1));
25568  }
25569  } else {
25570  if (_height>1) {
25571  if (_depth>1) for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
25572  res.draw_image(res.get_crop(0,0,0,0,h-1,d-1).haar(false,1));
25573  else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
25574  res.draw_image(res.get_crop(0,0,0,0,h-1,0).haar(false,1));
25575  } else {
25576  if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
25577  res.draw_image(res.get_crop(0,0,0,0,0,d-1).haar(false,1));
25578  else return *this;
25579  }
25580  }
25581  }
25582  return res;
25583  }
25584  return *this;
25585  }
25586 
25588 
25592  CImgList<Tfloat> get_FFT(const char axis, const bool is_invert=false) const {
25593  CImgList<Tfloat> res(*this,CImg<Tfloat>());
25594  CImg<Tfloat>::FFT(res[0],res[1],axis,is_invert);
25595  return res;
25596  }
25597 
25599  /*
25600  \param is_invert Tells if the forward (\c false) or inverse (\c true) FFT is computed.
25601  **/
25602  CImgList<Tfloat> get_FFT(const bool is_invert=false) const {
25603  CImgList<Tfloat> res(*this,CImg<Tfloat>());
25604  CImg<Tfloat>::FFT(res[0],res[1],is_invert);
25605  return res;
25606  }
25607 
25609 
25615  static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_invert=false) {
25616  if (!real)
25617  throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.",
25618  pixel_type());
25619 
25620  if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
25621  if (!real.is_sameXYZC(imag))
25622  throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
25623  pixel_type(),
25624  real._width,real._height,real._depth,real._spectrum,real._data,
25625  imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
25626 #ifdef cimg_use_fftw3
25627  fftw_complex *data_in;
25628  fftw_plan data_plan;
25629 
25630  switch (cimg::uncase(axis)) {
25631  case 'x' : { // Fourier along X, using FFTW library.
25632  data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width);
25633  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
25634  pixel_type(),
25635  cimg::strbuffersize(sizeof(fftw_complex)*real._width),
25636  real._width,real._height,real._depth,real._spectrum);
25637 
25638  data_plan = fftw_plan_dft_1d(real._width,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25639  cimg_forYZC(real,y,z,c) {
25640  T *ptrr = real.data(0,y,z,c), *ptri = imag.data(0,y,z,c);
25641  double *ptrd = (double*)data_in;
25642  cimg_forX(real,x) { *(ptrd++) = (double)*(ptrr++); *(ptrd++) = (double)*(ptri++); }
25643  fftw_execute(data_plan);
25644  const unsigned int fact = real._width;
25645  if (is_invert) cimg_forX(real,x) { *(--ptri) = (T)(*(--ptrd)/fact); *(--ptrr) = (T)(*(--ptrd)/fact); }
25646  else cimg_forX(real,x) { *(--ptri) = (T)*(--ptrd); *(--ptrr) = (T)*(--ptrd); }
25647  }
25648  } break;
25649  case 'y' : { // Fourier along Y, using FFTW library.
25650  data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._height);
25651  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the Y-axis.",
25652  pixel_type(),
25653  cimg::strbuffersize(sizeof(fftw_complex)*real._height),
25654  real._width,real._height,real._depth,real._spectrum);
25655 
25656  data_plan = fftw_plan_dft_1d(real._height,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25657  const unsigned int off = real._width;
25658  cimg_forXZC(real,x,z,c) {
25659  T *ptrr = real.data(x,0,z,c), *ptri = imag.data(x,0,z,c);
25660  double *ptrd = (double*)data_in;
25661  cimg_forY(real,y) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
25662  fftw_execute(data_plan);
25663  const unsigned int fact = real._height;
25664  if (is_invert) cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
25665  else cimg_forY(real,y) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
25666  }
25667  } break;
25668  case 'z' : { // Fourier along Z, using FFTW library.
25669  data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._depth);
25670  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the Z-axis.",
25671  pixel_type(),
25672  cimg::strbuffersize(sizeof(fftw_complex)*real._depth),
25673  real._width,real._height,real._depth,real._spectrum);
25674 
25675  data_plan = fftw_plan_dft_1d(real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25676  const unsigned long off = (unsigned long)real._width*real._height;
25677  cimg_forXYC(real,x,y,c) {
25678  T *ptrr = real.data(x,y,0,c), *ptri = imag.data(x,y,0,c);
25679  double *ptrd = (double*)data_in;
25680  cimg_forZ(real,z) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
25681  fftw_execute(data_plan);
25682  const unsigned int fact = real._depth;
25683  if (is_invert) cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
25684  else cimg_forZ(real,z) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
25685  }
25686  } break;
25687  default : { // Fourier along C, using FFTW library.
25688  data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * real._spectrum);
25689  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u) along the C-axis.",
25690  pixel_type(),
25691  cimg::strbuffersize(sizeof(fftw_complex)*real._spectrum),
25692  real._width,real._height,real._depth,real._spectrum);
25693 
25694  data_plan = fftw_plan_dft_1d(real._spectrum,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25695  const unsigned long off = (unsigned long)real._width*real._height*real._depth;
25696  cimg_forXYZ(real,x,y,z) {
25697  T *ptrr = real.data(x,y,z,0), *ptri = imag.data(x,y,z,0);
25698  double *ptrd = (double*)data_in;
25699  cimg_forC(real,c) { *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri; ptrr+=off; ptri+=off; }
25700  fftw_execute(data_plan);
25701  const unsigned int fact = real._spectrum;
25702  if (is_invert) cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)(*(--ptrd)/fact); *ptrr = (T)(*(--ptrd)/fact); }
25703  else cimg_forC(real,c) { ptrr-=off; ptri-=off; *ptri = (T)*(--ptrd); *ptrr = (T)*(--ptrd); }
25704  }
25705  }
25706  }
25707  fftw_destroy_plan(data_plan);
25708  fftw_free(data_in);
25709 #else
25710  switch (cimg::uncase(axis)) {
25711  case 'x' : { // Fourier along X, using built-in functions.
25712  const unsigned int N = real._width, N2 = (N>>1);
25713  if (((N-1)&N) && N!=1)
25714  throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the X-axis.",
25715  pixel_type(),
25716  real._width,real._height,real._depth,real._spectrum);
25717 
25718  for (unsigned int i = 0, j = 0; i<N2; ++i) {
25719  if (j>i) cimg_forYZC(real,y,z,c) {
25720  cimg::swap(real(i,y,z,c),real(j,y,z,c)); cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
25721  if (j<N2) {
25722  const unsigned int ri = N-1-i, rj = N-1-j;
25723  cimg::swap(real(ri,y,z,c),real(rj,y,z,c)); cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
25724  }
25725  }
25726  for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
25727  }
25728  for (unsigned int delta = 2; delta<=N; delta<<=1) {
25729  const unsigned int delta2 = (delta>>1);
25730  for (unsigned int i = 0; i<N; i+=delta) {
25731  float wr = 1, wi = 0;
25732  const float angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
25733  ca = (float)std::cos(angle),
25734  sa = (float)std::sin(angle);
25735  for (unsigned int k = 0; k<delta2; ++k) {
25736  const unsigned int j = i + k, nj = j + delta2;
25737  cimg_forYZC(real,y,z,c) {
25738  T &ir = real(j,y,z,c), &ii = imag(j,y,z,c), &nir = real(nj,y,z,c), &nii = imag(nj,y,z,c);
25739  const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
25740  nir = (T)(ir - tmpr);
25741  nii = (T)(ii - tmpi);
25742  ir+=(T)tmpr;
25743  ii+=(T)tmpi;
25744  }
25745  const float nwr = wr*ca-wi*sa;
25746  wi = wi*ca + wr*sa;
25747  wr = nwr;
25748  }
25749  }
25750  }
25751  if (is_invert) { real/=N; imag/=N; }
25752  } break;
25753  case 'y' : { // Fourier along Y, using built-in functions.
25754  const unsigned int N = real._height, N2 = (N>>1);
25755  if (((N-1)&N) && N!=1)
25756  throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Y-axis.",
25757  pixel_type(),
25758  real._width,real._height,real._depth,real._spectrum);
25759 
25760  for (unsigned int i = 0, j = 0; i<N2; ++i) {
25761  if (j>i) cimg_forXZC(real,x,z,c) {
25762  cimg::swap(real(x,i,z,c),real(x,j,z,c)); cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
25763  if (j<N2) {
25764  const unsigned int ri = N - 1 - i, rj = N - 1 - j;
25765  cimg::swap(real(x,ri,z,c),real(x,rj,z,c)); cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
25766  }
25767  }
25768  for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
25769  }
25770  for (unsigned int delta = 2; delta<=N; delta<<=1) {
25771  const unsigned int delta2 = (delta>>1);
25772  for (unsigned int i = 0; i<N; i+=delta) {
25773  float wr = 1, wi = 0;
25774  const float angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
25775  ca = (float)std::cos(angle), sa = (float)std::sin(angle);
25776  for (unsigned int k = 0; k<delta2; ++k) {
25777  const unsigned int j = i + k, nj = j + delta2;
25778  cimg_forXZC(real,x,z,c) {
25779  T &ir = real(x,j,z,c), &ii = imag(x,j,z,c), &nir = real(x,nj,z,c), &nii = imag(x,nj,z,c);
25780  const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
25781  nir = (T)(ir - tmpr);
25782  nii = (T)(ii - tmpi);
25783  ir+=(T)tmpr;
25784  ii+=(T)tmpi;
25785  }
25786  const float nwr = wr*ca-wi*sa;
25787  wi = wi*ca + wr*sa;
25788  wr = nwr;
25789  }
25790  }
25791  }
25792  if (is_invert) { real/=N; imag/=N; }
25793  } break;
25794  case 'z' : { // Fourier along Z, using built-in functions.
25795  const unsigned int N = real._depth, N2 = (N>>1);
25796  if (((N-1)&N) && N!=1)
25797  throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) have non 2^N dimension along the Z-axis.",
25798  pixel_type(),
25799  real._width,real._height,real._depth,real._spectrum);
25800 
25801  for (unsigned int i = 0, j = 0; i<N2; ++i) {
25802  if (j>i) cimg_forXYC(real,x,y,c) {
25803  cimg::swap(real(x,y,i,c),real(x,y,j,c)); cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
25804  if (j<N2) {
25805  const unsigned int ri = N - 1 - i, rj = N - 1 - j;
25806  cimg::swap(real(x,y,ri,c),real(x,y,rj,c)); cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
25807  }
25808  }
25809  for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
25810  }
25811  for (unsigned int delta = 2; delta<=N; delta<<=1) {
25812  const unsigned int delta2 = (delta>>1);
25813  for (unsigned int i = 0; i<N; i+=delta) {
25814  float wr = 1, wi = 0;
25815  const float angle = (float)((is_invert?+1:-1)*2*cimg::PI/delta),
25816  ca = (float)std::cos(angle), sa = (float)std::sin(angle);
25817  for (unsigned int k = 0; k<delta2; ++k) {
25818  const unsigned int j = i + k, nj = j + delta2;
25819  cimg_forXYC(real,x,y,c) {
25820  T &ir = real(x,y,j,c), &ii = imag(x,y,j,c), &nir = real(x,y,nj,c), &nii = imag(x,y,nj,c);
25821  const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
25822  nir = (T)(ir - tmpr);
25823  nii = (T)(ii - tmpi);
25824  ir+=(T)tmpr;
25825  ii+=(T)tmpi;
25826  }
25827  const float nwr = wr*ca-wi*sa;
25828  wi = wi*ca + wr*sa;
25829  wr = nwr;
25830  }
25831  }
25832  }
25833  if (is_invert) { real/=N; imag/=N; }
25834  } break;
25835  default :
25836  throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts (%u,%u,%u,%u) "
25837  "(should be { x | y | z }).",
25838  pixel_type(),axis,
25839  real._width,real._height,real._depth,real._spectrum);
25840  }
25841 #endif
25842  }
25843 
25845 
25850  static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_invert=false) {
25851  if (!real)
25852  throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.",
25853  pixel_type());
25854 
25855  if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,0);
25856  if (!real.is_sameXYZC(imag))
25857  throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
25858  pixel_type(),
25859  real._width,real._height,real._depth,real._spectrum,real._data,
25860  imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
25861 
25862 #ifdef cimg_use_fftw3
25863  fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
25864  if (!data_in) throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) for computing FFT of image (%u,%u,%u,%u).",
25865  pixel_type(),
25866  cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth*real._spectrum),
25867  real._width,real._height,real._depth,real._spectrum);
25868 
25869  fftw_plan data_plan;
25870  const unsigned long w = (unsigned long)real._width, wh = w*real._height, whd = wh*real._depth;
25871  data_plan = fftw_plan_dft_3d(real._width,real._height,real._depth,data_in,data_in,is_invert?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
25872  cimg_forC(real,c) {
25873  T *ptrr = real.data(0,0,0,c), *ptri = imag.data(0,0,0,c);
25874  double *ptrd = (double*)data_in;
25875  for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
25876  for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
25877  for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
25878  *(ptrd++) = (double)*ptrr; *(ptrd++) = (double)*ptri;
25879  }
25880  fftw_execute(data_plan);
25881  ptrd = (double*)data_in;
25882  ptrr = real.data(0,0,0,c);
25883  ptri = imag.data(0,0,0,c);
25884  if (!is_invert) for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
25885  for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
25886  for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
25887  *ptrr = (T)*(ptrd++); *ptri = (T)*(ptrd++);
25888  }
25889  else for (unsigned int x = 0; x<real._width; ++x, ptrr-=wh-1, ptri-=wh-1)
25890  for (unsigned int y = 0; y<real._height; ++y, ptrr-=whd-w, ptri-=whd-w)
25891  for (unsigned int z = 0; z<real._depth; ++z, ptrr+=wh, ptri+=wh) {
25892  *ptrr = (T)(*(ptrd++)/whd); *ptri = (T)(*(ptrd++)/whd);
25893  }
25894  }
25895  fftw_destroy_plan(data_plan);
25896  fftw_free(data_in);
25897 #else
25898  if (real._depth>1) FFT(real,imag,'z',is_invert);
25899  if (real._height>1) FFT(real,imag,'y',is_invert);
25900  if (real._width>1) FFT(real,imag,'x',is_invert);
25901 #endif
25902  }
25903 
25905  //-------------------------------------
25906  //
25908 
25909  //-------------------------------------
25910 
25912 
25917  CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
25918  if (_height!=3 || _depth>1 || _spectrum>1)
25919  throw CImgInstanceException(_cimg_instance
25920  "shift_object3d(): Instance is not a set of 3d vertices.",
25921  cimg_instance);
25922 
25923  get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
25924  return *this;
25925  }
25926 
25928  CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
25929  return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
25930  }
25931 
25933 
25937  if (_height!=3 || _depth>1 || _spectrum>1)
25938  throw CImgInstanceException(_cimg_instance
25939  "shift_object3d(): Instance is not a set of 3d vertices.",
25940  cimg_instance);
25941 
25942  CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
25943  float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
25944  xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
25945  return *this;
25946  }
25947 
25950  return CImg<Tfloat>(*this,false).shift_object3d();
25951  }
25952 
25954 
25959  CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
25960  if (_height!=3 || _depth>1 || _spectrum>1)
25961  throw CImgInstanceException(_cimg_instance
25962  "resize_object3d(): Instance is not a set of 3d vertices.",
25963  cimg_instance);
25964 
25965  CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
25966  float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
25967  if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
25968  if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
25969  if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
25970  return *this;
25971  }
25972 
25974  CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
25975  return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
25976  }
25977 
25980  if (_height!=3 || _depth>1 || _spectrum>1)
25981  throw CImgInstanceException(_cimg_instance
25982  "resize_object3d(): Instance is not a set of 3d vertices.",
25983  cimg_instance);
25984 
25985  CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
25986  float xm, xM = (float)xcoords.max_min(xm), ym, yM = (float)ycoords.max_min(ym), zm, zM = (float)zcoords.max_min(zm);
25987  const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
25988  if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
25989  return *this;
25990  }
25991 
25994  return CImg<Tfloat>(*this,false).resize_object3d();
25995  }
25996 
25998 
26003  template<typename tf, typename tp, typename tff>
26004  CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices, const CImgList<tff>& obj_primitives) {
26005  if (!obj_vertices || !obj_primitives) return *this;
26006  if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
26007  throw CImgInstanceException(_cimg_instance
26008  "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a set of 3d vertices.",
26009  cimg_instance,
26010  obj_vertices._width,obj_vertices._height,obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
26011 
26012  if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
26013  if (_height!=3 || _depth>1 || _spectrum>1)
26014  throw CImgInstanceException(_cimg_instance
26015  "append_object3d(): Instance is not a set of 3d vertices.",
26016  cimg_instance);
26017 
26018  const unsigned int P = _width;
26019  append(obj_vertices,'x');
26020  const unsigned int N = primitives._width;
26021  primitives.insert(obj_primitives);
26022  for (unsigned int i = N; i<primitives._width; ++i) {
26023  CImg<tf> &p = primitives[i];
26024  switch (p.size()) {
26025  case 1 : p[0]+=P; break; // Point.
26026  case 5 : p[0]+=P; p[1]+=P; break; // Sphere.
26027  case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment.
26028  case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle.
26029  case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle.
26030  }
26031  }
26032  return *this;
26033  }
26034 
26036 
26042  template<typename tp, typename tc, typename tt, typename tx>
26044  const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::empty()) const {
26045  if (is_empty()) return *this;
26046  if (_height!=3)
26047  throw CImgInstanceException(_cimg_instance
26048  "texturize_object3d(): image instance is not a set of 3d points.",
26049  cimg_instance);
26050  if (coords && (coords._width!=_width || coords._height!=2))
26051  throw CImgArgumentException(_cimg_instance
26052  "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
26053  cimg_instance,
26054  coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
26055  CImg<unsigned int> _coords;
26056  if (!coords) { // If no texture coordinates specified, do a default XY-projection.
26057  _coords.assign(_width,2);
26058  float
26059  xmin, xmax = (float)get_shared_row(0).max_min(xmin),
26060  ymin, ymax = (float)get_shared_row(1).max_min(ymin),
26061  dx = xmax>xmin?xmax-xmin:1,
26062  dy = ymax>ymin?ymax-ymin:1;
26063  cimg_forX(*this,p) {
26064  _coords(p,0) = (unsigned int)(((*this)(p,0)-xmin)*(texture._width-1)/dx);
26065  _coords(p,1) = (unsigned int)(((*this)(p,1)-ymin)*(texture._height-1)/dy);
26066  }
26067  } else _coords = coords;
26068 
26069  int texture_ind = -1;
26070  cimglist_for(primitives,l) {
26071  CImg<tp> &p = primitives[l];
26072  const unsigned int siz = p.size();
26073  switch (siz) {
26074  case 1 : { // Point.
26075  const unsigned int
26076  i0 = (unsigned int)p[0],
26077  x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1);
26078  texture.get_vector_at(x0,y0).move_to(colors[l]);
26079  } break;
26080  case 2 : case 6 : { // Line.
26081  const unsigned int
26082  i0 = (unsigned int)p[0], i1 = (unsigned int)p[1],
26083  x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
26084  x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1);
26085  if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
26086  CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
26087  } break;
26088  case 3 : case 9 : { // Triangle.
26089  const unsigned int
26090  i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2],
26091  x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
26092  x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1),
26093  x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1);
26094  if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
26095  CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
26096  } break;
26097  case 4 : case 12 : { // Quadrangle.
26098  const unsigned int
26099  i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3],
26100  x0 = (unsigned int)_coords(i0,0), y0 = (unsigned int)_coords(i0,1),
26101  x1 = (unsigned int)_coords(i1,0), y1 = (unsigned int)_coords(i1,1),
26102  x2 = (unsigned int)_coords(i2,0), y2 = (unsigned int)_coords(i2,1),
26103  x3 = (unsigned int)_coords(i3,0), y3 = (unsigned int)_coords(i3,1);
26104  if (texture_ind<0) colors[texture_ind=l] = texture; else colors[l].assign(colors[texture_ind],true);
26105  CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
26106  } break;
26107  }
26108  }
26109  return *this;
26110  }
26111 
26113 
26129  template<typename tf, typename tc, typename te>
26130  CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
26131  if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
26132  throw CImgArgumentException(_cimg_instance
26133  "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) "
26134  "have incompatible dimensions.",
26135  cimg_instance,
26136  elevation._width,elevation._height,elevation._depth,elevation._spectrum,elevation._data);
26137  if (is_empty()) return *this;
26138  float m, M = (float)max_min(m);
26139  if (M==m) ++M;
26140  colors.assign();
26141  const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
26142  for (unsigned int y = 0; y<size_y1; ++y)
26143  for (unsigned int x = 0; x<size_x1; ++x) {
26144  const unsigned char
26145  r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
26146  g = _spectrum>1?(unsigned char)(((*this)(x,y,1) - m)*255/(M-m)):r,
26147  b = _spectrum>2?(unsigned char)(((*this)(x,y,2) - m)*255/(M-m)):(_spectrum>1?0:r);
26148  CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
26149  }
26150  const typename CImg<te>::_functor2d_int func(elevation);
26151  return elevation3d(primitives,func,0,0,_width-1.0f,_height-1.0f,_width,_height);
26152  }
26153 
26155 
26163  template<typename tf, typename tc>
26165  const unsigned int x0, const unsigned int y0, const unsigned int z0,
26166  const bool normalize_colors=false) const {
26167  float m = 0, M = 0, delta = 1;
26168  if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
26169  const unsigned int
26170  _x0 = (x0>=_width)?_width - 1:x0,
26171  _y0 = (y0>=_height)?_height - 1:y0,
26172  _z0 = (z0>=_depth)?_depth - 1:z0;
26173  CImg<tc> img_xy, img_xz, img_yz;
26174  if (normalize_colors) {
26175  ((get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1)-=m)*=delta).move_to(img_xy);
26176  ((get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1)-=m)*=delta).resize(_width,_depth,1,-100,-1).move_to(img_xz);
26177  ((get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1)-=m)*=delta).resize(_height,_depth,1,-100,-1).move_to(img_yz);
26178  } else {
26179  get_crop(0,0,_z0,0,_width-1,_height-1,_z0,_spectrum-1).move_to(img_xy);
26180  get_crop(0,_y0,0,0,_width-1,_y0,_depth-1,_spectrum-1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
26181  get_crop(_x0,0,0,0,_x0,_height-1,_depth-1,_spectrum-1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
26182  }
26183  CImg<floatT> points(12,3,1,1,
26184  0,_width-1,_width-1,0, 0,_width-1,_width-1,0, _x0,_x0,_x0,_x0,
26185  0,0,_height-1,_height-1, _y0,_y0,_y0,_y0, 0,_height-1,_height-1,0,
26186  _z0,_z0,_z0,_z0, 0,0,_depth-1,_depth-1, 0,0,_depth-1,_depth-1);
26187  primitives.assign();
26188  CImg<tf>::vector(0,1,2,3,0,0,img_xy._width-1,0,img_xy._width-1,img_xy._height-1,0,img_xy._height-1).move_to(primitives);
26189  CImg<tf>::vector(4,5,6,7,0,0,img_xz._width-1,0,img_xz._width-1,img_xz._height-1,0,img_xz._height-1).move_to(primitives);
26190  CImg<tf>::vector(8,9,10,11,0,0,img_yz._width-1,0,img_yz._width-1,img_yz._height-1,0,img_yz._height-1).move_to(primitives);
26191  colors.assign();
26192  img_xy.move_to(colors);
26193  img_xz.move_to(colors);
26194  img_yz.move_to(colors);
26195  return points;
26196  }
26197 
26199 
26215  template<typename tf>
26216  CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
26217  const int size_x=-100, const int size_y=-100) const {
26218  if (_spectrum>1)
26219  throw CImgInstanceException(_cimg_instance
26220  "get_isoline3d(): Instance is not a scalar image.",
26221  cimg_instance);
26222  if (_depth>1)
26223  throw CImgInstanceException(_cimg_instance
26224  "get_isoline3d(): Instance is not a 2d image.",
26225  cimg_instance);
26226  primitives.assign();
26227  if (is_empty()) return *this;
26228  CImg<floatT> vertices;
26229  if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
26230  const _functor2d_int func(*this);
26231  vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,width(),height());
26232  } else {
26233  const _functor2d_float func(*this);
26234  vertices = isoline3d(primitives,func,isovalue,0,0,width()-1.0f,height()-1.0f,size_x,size_y);
26235  }
26236  return vertices;
26237  }
26238 
26240 
26257  template<typename tf>
26258  CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
26259  const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
26260  if (_spectrum>1)
26261  throw CImgInstanceException(_cimg_instance
26262  "get_isosurface3d(): Instance is not a scalar image.",
26263  cimg_instance);
26264  primitives.assign();
26265  if (is_empty()) return *this;
26266  CImg<floatT> vertices;
26267  if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
26268  const _functor3d_int func(*this);
26269  vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,width(),height(),depth());
26270  } else {
26271  const _functor3d_float func(*this);
26272  vertices = isosurface3d(primitives,func,isovalue,0,0,0,width()-1.0f,height()-1.0f,depth()-1.0f,size_x,size_y,size_z);
26273  }
26274  return vertices;
26275  }
26276 
26278 
26288  template<typename tf, typename tfunc>
26289  static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
26290  const float x0, const float y0, const float x1, const float y1,
26291  const int size_x=256, const int size_y=256) {
26292  const float
26293  nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
26294  nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
26295  const unsigned int
26296  _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100), nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
26297  _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100), nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
26298  if (nsize_x<2 || nsize_y<2)
26299  throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).",
26300  pixel_type(),
26301  nsize_x,nsize_y);
26302 
26303  CImg<floatT> vertices(nsize_x*nsize_y,3);
26304  floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
26305  for (unsigned int y = 0; y<nsize_y; ++y) {
26306  const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
26307  for (unsigned int x = 0; x<nsize_x; ++x) {
26308  const float X = nx0 + x*(nx1-nx0)/nsize_x1;
26309  *(ptr_x++) = (float)x;
26310  *(ptr_y++) = (float)y;
26311  *(ptr_z++) = (float)func(X,Y);
26312  }
26313  }
26314  primitives.assign(nsize_x1*nsize_y1,1,4);
26315  for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
26316  const unsigned int yw = y*nsize_x;
26317  for (unsigned int x = 0; x<nsize_x1; ++x) {
26318  const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
26319  primitives[p++].fill(xpyw,xpyww,xpyww+1,xpyw+1);
26320  }
26321  }
26322  return vertices;
26323  }
26324 
26326  template<typename tf>
26327  static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
26328  const float x0, const float y0, const float x1, const float y1,
26329  const int size_x=256, const int size_y=256) {
26330  const _functor2d_expr func(expression);
26331  return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
26332  }
26333 
26335 
26347  template<typename tf, typename tfunc>
26348  static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
26349  const float x0, const float y0, const float x1, const float y1,
26350  const int size_x=256, const int size_y=256) {
26351  static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc, 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
26352  static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
26353  { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 },
26354  { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 },
26355  { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } };
26356  const unsigned int
26357  _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
26358  _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
26359  nx = _nx?_nx:1,
26360  ny = _ny?_ny:1,
26361  nxm1 = nx - 1,
26362  nym1 = ny - 1;
26363  primitives.assign();
26364  if (!nxm1 || !nym1) return CImg<floatT>();
26365  const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
26366  CImgList<floatT> vertices;
26367  CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
26368  CImg<floatT> values1(nx), values2(nx);
26369  float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
26370 
26371  // Fill first line with values
26372  cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
26373 
26374  // Run the marching squares algorithm
26375  for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
26376  X = x0; nX = X + dx;
26377  indices2.fill(-1);
26378  for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
26379 
26380  // Determine square configuration
26381  const float
26382  val0 = values1(xi),
26383  val1 = values1(nxi),
26384  val2 = values2(nxi) = (float)func(nX,nY),
26385  val3 = values2(xi) = (float)func(X,nY);
26386  const unsigned int
26387  configuration = (val0<isovalue?1:0) | (val1<isovalue?2:0) | (val2<isovalue?4:0) | (val3<isovalue?8:0),
26388  edge = edges[configuration];
26389 
26390  // Compute intersection vertices
26391  if (edge) {
26392  if ((edge&1) && indices1(xi,0)<0) {
26393  const float Xi = X + (isovalue-val0)*dx/(val1-val0);
26394  indices1(xi,0) = vertices._width;
26395  CImg<floatT>::vector(Xi,Y,0).move_to(vertices);
26396  }
26397  if ((edge&2) && indices1(nxi,1)<0) {
26398  const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
26399  indices1(nxi,1) = vertices._width;
26400  CImg<floatT>::vector(nX,Yi,0).move_to(vertices);
26401  }
26402  if ((edge&4) && indices2(xi,0)<0) {
26403  const float Xi = X + (isovalue-val3)*dx/(val2-val3);
26404  indices2(xi,0) = vertices._width;
26405  CImg<floatT>::vector(Xi,nY,0).move_to(vertices);
26406  }
26407  if ((edge&8) && indices1(xi,1)<0) {
26408  const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
26409  indices1(xi,1) = vertices._width;
26410  CImg<floatT>::vector(X,Yi,0).move_to(vertices);
26411  }
26412 
26413  // Create segments
26414  for (const int *segment = segments[configuration]; *segment!=-1; ) {
26415  const unsigned int p0 = *(segment++), p1 = *(segment++);
26416  const tf
26417  i0 = (tf)(_isoline3d_indice(p0,indices1,indices2,xi,nxi)),
26418  i1 = (tf)(_isoline3d_indice(p1,indices1,indices2,xi,nxi));
26419  CImg<tf>::vector(i0,i1).move_to(primitives);
26420  }
26421  }
26422  }
26423  values1.swap(values2);
26424  indices1.swap(indices2);
26425  }
26426  return vertices>'x';
26427  }
26428 
26430  template<typename tf>
26431  static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
26432  const float x0, const float y0, const float x1, const float y1,
26433  const int size_x=256, const int size_y=256) {
26434  const _functor2d_expr func(expression);
26435  return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
26436  }
26437 
26438  template<typename t>
26439  static int _isoline3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
26440  const unsigned int x, const unsigned int nx) {
26441  switch (edge) {
26442  case 0 : return (int)indices1(x,0);
26443  case 1 : return (int)indices1(nx,1);
26444  case 2 : return (int)indices2(x,0);
26445  case 3 : return (int)indices1(x,1);
26446  }
26447  return 0;
26448  }
26449 
26451 
26466  template<typename tf, typename tfunc>
26467  static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
26468  const float x0, const float y0, const float z0,
26469  const float x1, const float y1, const float z1,
26470  const int size_x=32, const int size_y=32, const int size_z=32) {
26471  static unsigned int edges[256] = {
26472  0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
26473  0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
26474  0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
26475  0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
26476  0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
26477  0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
26478  0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
26479  0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
26480  0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
26481  0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
26482  0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
26483  0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
26484  0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
26485  0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
26486  0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
26487  0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000 };
26488 
26489  static int triangles[256][16] = {
26490  { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26491  { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26492  { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26493  { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
26494  { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26495  { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
26496  { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
26497  { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26498  { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26499  { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
26500  { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
26501  { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
26502  { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
26503  { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
26504  { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 }, { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
26505  { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
26506  { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26507  { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
26508  { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
26509  { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
26510  { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
26511  { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 }, { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
26512  { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
26513  { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 }, { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
26514  { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
26515  { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 }, { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26516  { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 }, { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
26517  { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 }, { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
26518  { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
26519  { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 }, { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
26520  { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 }, { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
26521  { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 }, { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26522  { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26523  { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
26524  { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
26525  { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
26526  { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
26527  { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
26528  { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
26529  { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 }, { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
26530  { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
26531  { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
26532  { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
26533  { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 }, { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
26534  { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 }, { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
26535  { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 }, { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
26536  { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 }, { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
26537  { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 }, { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
26538  { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
26539  { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
26540  { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
26541  { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
26542  { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
26543  { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 }, { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
26544  { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 }, { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
26545  { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 }, { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26546  { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 }, { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
26547  { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 }, { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
26548  { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
26549  { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 }, { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26550  { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 }, { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
26551  { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 }, { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
26552  { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 }, { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26553  { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 }, { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26554  { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26555  { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
26556  { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
26557  { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
26558  { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
26559  { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 }, { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
26560  { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
26561  { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 }, { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
26562  { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
26563  { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
26564  { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 }, { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
26565  { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 }, { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
26566  { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26567  { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 }, { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
26568  { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 }, { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
26569  { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 }, { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26570  { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
26571  { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
26572  { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 }, { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
26573  { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 }, { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
26574  { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
26575  { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 }, { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
26576  { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 }, { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
26577  { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 }, { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
26578  { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
26579  { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 }, { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
26580  { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 }, { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
26581  { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 }, { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
26582  { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 }, { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
26583  { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 }, { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26584  { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 }, { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
26585  { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26586  { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
26587  { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 }, { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
26588  { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
26589  { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 }, { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
26590  { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 }, { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
26591  { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 }, { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
26592  { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
26593  { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 }, { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26594  { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
26595  { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 }, { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
26596  { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 }, { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
26597  { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 }, { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26598  { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 }, { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
26599  { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 }, { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
26600  { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26601  { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 }, { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26602  { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
26603  { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 }, { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
26604  { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 }, { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
26605  { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 }, { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
26606  { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 }, { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
26607  { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 }, { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26608  { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 }, { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
26609  { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26610  { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
26611  { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 }, { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26612  { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 }, { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
26613  { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26614  { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 }, { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26615  { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 }, { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26616  { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
26617  { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
26618  };
26619 
26620  const unsigned int
26621  _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
26622  _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
26623  _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
26624  nx = _nx?_nx:1,
26625  ny = _ny?_ny:1,
26626  nz = _nz?_nz:1,
26627  nxm1 = nx - 1,
26628  nym1 = ny - 1,
26629  nzm1 = nz - 1;
26630  primitives.assign();
26631  if (!nxm1 || !nym1 || !nzm1) return CImg<floatT>();
26632  const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
26633  CImgList<floatT> vertices;
26634  CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
26635  CImg<floatT> values1(nx,ny), values2(nx,ny);
26636  float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
26637 
26638  // Fill the first plane with function values
26639  Y = y0;
26640  cimg_forY(values1,y) {
26641  X = x0;
26642  cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
26643  Y+=dy;
26644  }
26645 
26646  // Run Marching Cubes algorithm
26647  Z = z0; nZ = Z + dz;
26648  for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
26649  Y = y0; nY = Y + dy;
26650  indices2.fill(-1);
26651  for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
26652  X = x0; nX = X + dx;
26653  for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
26654 
26655  // Determine cube configuration
26656  const float
26657  val0 = values1(xi,yi),
26658  val1 = values1(nxi,yi),
26659  val2 = values1(nxi,nyi),
26660  val3 = values1(xi,nyi),
26661  val4 = values2(xi,yi) = (float)func(X,Y,nZ),
26662  val5 = values2(nxi,yi) = (float)func(nX,Y,nZ),
26663  val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
26664  val7 = values2(xi,nyi) = (float)func(X,nY,nZ);
26665 
26666  const unsigned int configuration =
26667  (val0<isovalue?1:0) | (val1<isovalue?2:0) | (val2<isovalue?4:0) | (val3<isovalue?8:0) |
26668  (val4<isovalue?16:0) | (val5<isovalue?32:0) | (val6<isovalue?64:0) | (val7<isovalue?128:0),
26669  edge = edges[configuration];
26670 
26671  // Compute intersection vertices
26672  if (edge) {
26673  if ((edge&1) && indices1(xi,yi,0)<0) {
26674  const float Xi = X + (isovalue-val0)*dx/(val1-val0);
26675  indices1(xi,yi,0) = vertices._width;
26676  CImg<floatT>::vector(Xi,Y,Z).move_to(vertices);
26677  }
26678  if ((edge&2) && indices1(nxi,yi,1)<0) {
26679  const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
26680  indices1(nxi,yi,1) = vertices._width;
26681  CImg<floatT>::vector(nX,Yi,Z).move_to(vertices);
26682  }
26683  if ((edge&4) && indices1(xi,nyi,0)<0) {
26684  const float Xi = X + (isovalue-val3)*dx/(val2-val3);
26685  indices1(xi,nyi,0) = vertices._width;
26686  CImg<floatT>::vector(Xi,nY,Z).move_to(vertices);
26687  }
26688  if ((edge&8) && indices1(xi,yi,1)<0) {
26689  const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
26690  indices1(xi,yi,1) = vertices._width;
26691  CImg<floatT>::vector(X,Yi,Z).move_to(vertices);
26692  }
26693  if ((edge&16) && indices2(xi,yi,0)<0) {
26694  const float Xi = X + (isovalue-val4)*dx/(val5-val4);
26695  indices2(xi,yi,0) = vertices._width;
26696  CImg<floatT>::vector(Xi,Y,nZ).move_to(vertices);
26697  }
26698  if ((edge&32) && indices2(nxi,yi,1)<0) {
26699  const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
26700  indices2(nxi,yi,1) = vertices._width;
26701  CImg<floatT>::vector(nX,Yi,nZ).move_to(vertices);
26702  }
26703  if ((edge&64) && indices2(xi,nyi,0)<0) {
26704  const float Xi = X + (isovalue-val7)*dx/(val6-val7);
26705  indices2(xi,nyi,0) = vertices._width;
26706  CImg<floatT>::vector(Xi,nY,nZ).move_to(vertices);
26707  }
26708  if ((edge&128) && indices2(xi,yi,1)<0) {
26709  const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
26710  indices2(xi,yi,1) = vertices._width;
26711  CImg<floatT>::vector(X,Yi,nZ).move_to(vertices);
26712  }
26713  if ((edge&256) && indices1(xi,yi,2)<0) {
26714  const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
26715  indices1(xi,yi,2) = vertices._width;
26716  CImg<floatT>::vector(X,Y,Zi).move_to(vertices);
26717  }
26718  if ((edge&512) && indices1(nxi,yi,2)<0) {
26719  const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
26720  indices1(nxi,yi,2) = vertices._width;
26721  CImg<floatT>::vector(nX,Y,Zi).move_to(vertices);
26722  }
26723  if ((edge&1024) && indices1(nxi,nyi,2)<0) {
26724  const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
26725  indices1(nxi,nyi,2) = vertices._width;
26726  CImg<floatT>::vector(nX,nY,Zi).move_to(vertices);
26727  }
26728  if ((edge&2048) && indices1(xi,nyi,2)<0) {
26729  const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
26730  indices1(xi,nyi,2) = vertices._width;
26731  CImg<floatT>::vector(X,nY,Zi).move_to(vertices);
26732  }
26733 
26734  // Create triangles
26735  for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
26736  const unsigned int p0 = *(triangle++), p1 = *(triangle++), p2 = *(triangle++);
26737  const tf
26738  i0 = (tf)(_isosurface3d_indice(p0,indices1,indices2,xi,yi,nxi,nyi)),
26739  i1 = (tf)(_isosurface3d_indice(p1,indices1,indices2,xi,yi,nxi,nyi)),
26740  i2 = (tf)(_isosurface3d_indice(p2,indices1,indices2,xi,yi,nxi,nyi));
26741  CImg<tf>::vector(i0,i2,i1).move_to(primitives);
26742  }
26743  }
26744  }
26745  }
26746  cimg::swap(values1,values2);
26747  cimg::swap(indices1,indices2);
26748  }
26749  return vertices>'x';
26750  }
26751 
26753  template<typename tf>
26754  static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
26755  const float x0, const float y0, const float z0,
26756  const float x1, const float y1, const float z1,
26757  const int dx=32, const int dy=32, const int dz=32) {
26758  const _functor3d_expr func(expression);
26759  return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
26760  }
26761 
26762  template<typename t>
26763  static int _isosurface3d_indice(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
26764  const unsigned int x, const unsigned int y, const unsigned int nx, const unsigned int ny) {
26765  switch (edge) {
26766  case 0 : return indices1(x,y,0);
26767  case 1 : return indices1(nx,y,1);
26768  case 2 : return indices1(x,ny,0);
26769  case 3 : return indices1(x,y,1);
26770  case 4 : return indices2(x,y,0);
26771  case 5 : return indices2(nx,y,1);
26772  case 6 : return indices2(x,ny,0);
26773  case 7 : return indices2(x,y,1);
26774  case 8 : return indices1(x,y,2);
26775  case 9 : return indices1(nx,y,2);
26776  case 10 : return indices1(nx,ny,2);
26777  case 11 : return indices1(x,ny,2);
26778  }
26779  return 0;
26780  }
26781 
26782  // Define functors for accessing image values (used in previous functions).
26784  const CImg<T>& ref;
26785  _functor2d_int(const CImg<T>& pref):ref(pref) {}
26786  float operator()(const float x, const float y) const {
26787  return (float)ref((int)x,(int)y);
26788  }
26789  };
26790 
26792  const CImg<T>& ref;
26793  _functor2d_float(const CImg<T>& pref):ref(pref) {}
26794  float operator()(const float x, const float y) const {
26795  return (float)ref._linear_atXY(x,y);
26796  }
26797  };
26798 
26800  _cimg_math_parser *mp;
26801  _functor2d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
26802  ~_functor2d_expr() { delete mp; }
26803  float operator()(const float x, const float y) const {
26804  return (float)mp->eval(x,y,0,0);
26805  }
26806  };
26807 
26809  const CImg<T>& ref;
26810  _functor3d_int(const CImg<T>& pref):ref(pref) {}
26811  float operator()(const float x, const float y, const float z) const {
26812  return (float)ref((int)x,(int)y,(int)z);
26813  }
26814  };
26815 
26817  const CImg<T>& ref;
26818  _functor3d_float(const CImg<T>& pref):ref(pref) {}
26819  float operator()(const float x, const float y, const float z) const {
26820  return (float)ref._linear_atXYZ(x,y,z);
26821  }
26822  };
26823 
26825  _cimg_math_parser *mp;
26826  ~_functor3d_expr() { delete mp; }
26827  _functor3d_expr(const char *const expr):mp(0) { mp = new _cimg_math_parser(CImg<T>::empty(),expr,0); }
26828  float operator()(const float x, const float y, const float z) const {
26829  return (float)mp->eval(x,y,z,0);
26830  }
26831  };
26832 
26834  const CImg<T>& ref;
26835  _functor4d_int(const CImg<T>& pref):ref(pref) {}
26836  float operator()(const float x, const float y, const float z, const unsigned int c) const {
26837  return (float)ref((int)x,(int)y,(int)z,c);
26838  }
26839  };
26840 
26842 
26857  template<typename tf>
26858  static CImg<floatT> box3d(CImgList<tf>& primitives,
26859  const float size_x=200, const float size_y=100, const float size_z=100) {
26860  primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5);
26861  return CImg<floatT>(8,3,1,1,
26862  0.,size_x,size_x, 0., 0.,size_x,size_x, 0.,
26863  0., 0.,size_y,size_y, 0., 0.,size_y,size_y,
26864  0., 0., 0., 0.,size_z,size_z,size_z,size_z);
26865  }
26866 
26868 
26883  template<typename tf>
26884  static CImg<floatT> cone3d(CImgList<tf>& primitives,
26885  const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
26886  primitives.assign();
26887  if (!subdivisions) return CImg<floatT>();
26888  CImgList<floatT> vertices(2,1,3,1,1,
26889  0.,0.,size_z,
26890  0.,0.,0.);
26891  for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
26892  const float a = (float)(angle*cimg::PI/180);
26893  CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
26894  }
26895  const unsigned int nbr = vertices._width - 2;
26896  for (unsigned int p = 0; p<nbr; ++p) {
26897  const unsigned int curr = 2 + p, next = 2 + ((p+1)%nbr);
26898  CImg<tf>::vector(1,next,curr).move_to(primitives);
26899  CImg<tf>::vector(0,curr,next).move_to(primitives);
26900  }
26901  return vertices>'x';
26902  }
26903 
26905 
26920  template<typename tf>
26922  const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
26923  primitives.assign();
26924  if (!subdivisions) return CImg<floatT>();
26925  CImgList<floatT> vertices(2,1,3,1,1,
26926  0.,0.,0.,
26927  0.,0.,size_z);
26928  for (float delta = 360.0f/subdivisions, angle = 0; angle<360; angle+=delta) {
26929  const float a = (float)(angle*cimg::PI/180);
26930  CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.0f).move_to(vertices);
26931  CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
26932  }
26933  const unsigned int nbr = (vertices._width - 2)/2;
26934  for (unsigned int p = 0; p<nbr; ++p) {
26935  const unsigned int curr = 2+2*p, next = 2+(2*((p+1)%nbr));
26936  CImg<tf>::vector(0,next,curr).move_to(primitives);
26937  CImg<tf>::vector(1,curr+1,next+1).move_to(primitives);
26938  CImg<tf>::vector(curr,next,next+1,curr+1).move_to(primitives);
26939  }
26940  return vertices>'x';
26941  }
26942 
26944 
26960  template<typename tf>
26961  static CImg<floatT> torus3d(CImgList<tf>& primitives,
26962  const float radius1=100, const float radius2=30,
26963  const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
26964  primitives.assign();
26965  if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
26966  CImgList<floatT> vertices;
26967  for (unsigned int v = 0; v<subdivisions1; ++v) {
26968  const float
26969  beta = (float)(v*2*cimg::PI/subdivisions1),
26970  xc = radius1*(float)std::cos(beta),
26971  yc = radius1*(float)std::sin(beta);
26972  for (unsigned int u = 0; u<subdivisions2; ++u) {
26973  const float
26974  alpha = (float)(u*2*cimg::PI/subdivisions2),
26975  x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
26976  y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
26977  z = radius2*(float)std::sin(alpha);
26978  CImg<floatT>::vector(x,y,z).move_to(vertices);
26979  }
26980  }
26981  for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
26982  const unsigned int nv = (vv+1)%subdivisions1;
26983  for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
26984  const unsigned int nu = (uu+1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
26985  CImg<tf>::vector(svv+nu,svv+uu,snv+uu,snv+nu).move_to(primitives);
26986  }
26987  }
26988  return vertices>'x';
26989  }
26990 
26992 
27008  template<typename tf>
27009  static CImg<floatT> plane3d(CImgList<tf>& primitives,
27010  const float size_x=100, const float size_y=100,
27011  const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
27012  primitives.assign();
27013  if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
27014  CImgList<floatT> vertices;
27015  const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
27016  const float fx = (float)size_x/w, fy = (float)size_y/h;
27017  for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
27018  CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
27019  for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
27020  const int off1 = x+y*w, off2 = x+1+y*w, off3 = x+1+(y+1)*w, off4 = x+(y+1)*w;
27021  CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
27022  }
27023  return vertices>'x';
27024  }
27025 
27027 
27041  template<typename tf>
27042  static CImg<floatT> sphere3d(CImgList<tf>& primitives,
27043  const float radius=50, const unsigned int subdivisions=3) {
27044 
27045  // Create initial icosahedron
27046  primitives.assign();
27047  const double tmp = (1+std::sqrt(5.0f))/2, a = 1.0/std::sqrt(1+tmp*tmp), b = tmp*a;
27048  CImgList<floatT> vertices(12,1,3,1,1, b,a,0.0, -b,a,0.0, -b,-a,0.0, b,-a,0.0, a,0.0,b, a,0.0,-b,
27049  -a,0.0,-b, -a,0.0,b, 0.0,b,a, 0.0,-b,a, 0.0,-b,-a, 0.0,b,-a);
27050  primitives.assign(20,1,3,1,1, 4,8,7, 4,7,9, 5,6,11, 5,10,6, 0,4,3, 0,3,5, 2,7,1, 2,1,6,
27051  8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
27052  5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
27053  // edge - length/2
27054  float he = (float)a;
27055 
27056  // Recurse subdivisions
27057  for (unsigned int i = 0; i<subdivisions; ++i) {
27058  const unsigned int L = primitives._width;
27059  he/=2;
27060  const float he2 = he*he;
27061  for (unsigned int l = 0; l<L; ++l) {
27062  const unsigned int
27063  p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
27064  const float
27065  x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
27066  x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
27067  x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
27068  tnx0 = (x0+x1)/2, tny0 = (y0+y1)/2, tnz0 = (z0+z1)/2, nn0 = (float)std::sqrt(tnx0*tnx0+tny0*tny0+tnz0*tnz0),
27069  tnx1 = (x0+x2)/2, tny1 = (y0+y2)/2, tnz1 = (z0+z2)/2, nn1 = (float)std::sqrt(tnx1*tnx1+tny1*tny1+tnz1*tnz1),
27070  tnx2 = (x1+x2)/2, tny2 = (y1+y2)/2, tnz2 = (z1+z2)/2, nn2 = (float)std::sqrt(tnx2*tnx2+tny2*tny2+tnz2*tnz2),
27071  nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
27072  nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
27073  nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
27074  int i0 = -1, i1 = -1, i2 = -1;
27075  cimglist_for(vertices,p) {
27076  const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
27077  if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
27078  if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
27079  if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
27080  }
27081  if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices._width - 1; }
27082  if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices._width - 1; }
27083  if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices._width - 1; }
27084  primitives.remove(0);
27085  CImg<tf>::vector(p0,i0,i1).move_to(primitives);
27086  CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
27087  CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
27088  CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
27089  }
27090  }
27091  return (vertices>'x')*=radius;
27092  }
27093 
27095 
27110  template<typename tf, typename t>
27112  const CImg<t>& tensor, const unsigned int subdivisions=3) {
27113  primitives.assign();
27114  if (!subdivisions) return CImg<floatT>();
27115  CImg<floatT> S, V;
27116  tensor.symmetric_eigen(S,V);
27117  const float orient =
27118  (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
27119  (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
27120  (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
27121  if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
27122  const float l0 = S[0], l1 = S[1], l2 = S[2];
27123  CImg<floatT> vertices = sphere3d(primitives,1.0,subdivisions);
27124  vertices.get_shared_row(0)*=l0;
27125  vertices.get_shared_row(1)*=l1;
27126  vertices.get_shared_row(2)*=l2;
27127  return V*vertices;
27128  }
27129 
27131 
27136  template<typename tp, typename tc, typename to>
27138  const CImgList<tc>& colors,
27139  const to& opacities) {
27140  return get_object3dtoCImg3d(primitives,colors,opacities).move_to(*this);
27141  }
27142 
27144  template<typename tp, typename tc>
27146  const CImgList<tc>& colors) {
27147  return get_object3dtoCImg3d(primitives,colors).move_to(*this);
27148  }
27149 
27151  template<typename tp>
27152  CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives) {
27153  return get_object3dtoCImg3d(primitives).move_to(*this);
27154  }
27155 
27158  return get_object3dtoCImg3d().move_to(*this);
27159  }
27160 
27162  template<typename tp, typename tc, typename to>
27164  const CImgList<tc>& colors,
27165  const to& opacities) const {
27166  char error_message[1024] = { 0 };
27167  if (!is_object3d(primitives,colors,opacities,true,error_message))
27168  throw CImgInstanceException(_cimg_instance
27169  "object3dtoCImg3d(): Invalid specified 3d object (%u,%u) (%s).",
27170  cimg_instance,_width,primitives._width,error_message);
27171  CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
27172  float *ptrd = res._data;
27173 
27174  // Put magick number.
27175  *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
27176  *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
27177 
27178  // Put number of vertices and primitives.
27179  *(ptrd++) = cimg::uint2float(_width);
27180  *(ptrd++) = cimg::uint2float(primitives._width);
27181 
27182  // Put vertex data.
27183  if (is_empty() || !primitives) return res;
27184  const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
27185  cimg_forX(*this,p) {
27186  *(ptrd++) = (float)*(ptrx++);
27187  *(ptrd++) = (float)*(ptry++);
27188  *(ptrd++) = (float)*(ptrz++);
27189  }
27190 
27191  // Put primitive data.
27192  cimglist_for(primitives,p) {
27193  *(ptrd++) = (float)primitives[p].size();
27194  const tp *ptrp = primitives[p]._data;
27195  cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
27196  }
27197 
27198  // Put color/texture data.
27199  const unsigned int csiz = cimg::min(colors._width,primitives._width);
27200  for (int c = 0; c<(int)csiz; ++c) {
27201  const CImg<tc>& color = colors[c];
27202  const tc *ptrc = color._data;
27203  if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
27204  else {
27205  *(ptrd++) = -128.0f;
27206  int shared_ind = -1;
27207  if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
27208  if (shared_ind<0) {
27209  *(ptrd++) = (float)color._width;
27210  *(ptrd++) = (float)color._height;
27211  *(ptrd++) = (float)color._spectrum;
27212  cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
27213  } else {
27214  *(ptrd++) = (float)shared_ind;
27215  *(ptrd++) = 0;
27216  *(ptrd++) = 0;
27217  }
27218  }
27219  }
27220  const int csiz2 = primitives._width - colors._width;
27221  for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; *(ptrd++) = 200.0f; }
27222 
27223  // Put opacity data.
27224  ptrd = _object3dtoCImg3d(opacities,ptrd);
27225  const float *ptre = res.end();
27226  while (ptrd<ptre) *(ptrd++) = 1.0f;
27227  return res;
27228  }
27229 
27230  template<typename to>
27231  float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
27232  cimglist_for(opacities,o) {
27233  const CImg<to>& opacity = opacities[o];
27234  const to *ptro = opacity._data;
27235  if (opacity.size()==1) *(ptrd++) = (float)*ptro;
27236  else {
27237  *(ptrd++) = -128.0f;
27238  int shared_ind = -1;
27239  if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
27240  if (shared_ind<0) {
27241  *(ptrd++) = (float)opacity._width;
27242  *(ptrd++) = (float)opacity._height;
27243  *(ptrd++) = (float)opacity._spectrum;
27244  cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
27245  } else {
27246  *(ptrd++) = (float)shared_ind;
27247  *(ptrd++) = 0;
27248  *(ptrd++) = 0;
27249  }
27250  }
27251  }
27252  return ptrd;
27253  }
27254 
27255  template<typename to>
27256  float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
27257  const to *ptro = opacities._data;
27258  cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
27259  return ptrd;
27260  }
27261 
27262  template<typename tp, typename tc, typename to>
27263  unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
27264  const CImgList<tc>& colors,
27265  const CImgList<to>& opacities) const {
27266  unsigned int siz = 8 + 3*width();
27267  cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
27268  for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
27269  if (colors[c].is_shared()) siz+=4;
27270  else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3; }
27271  }
27272  if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
27273  cimglist_for(opacities,o) {
27274  if (opacities[o].is_shared()) siz+=4;
27275  else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4+osiz:1; }
27276  }
27277  siz+=primitives._width - opacities._width;
27278  return siz;
27279  }
27280 
27281  template<typename tp, typename tc, typename to>
27282  unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
27283  const CImgList<tc>& colors,
27284  const CImg<to>& opacities) const {
27285  unsigned int siz = 8 + 3*width();
27286  cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
27287  for (int c = cimg::min(primitives._width,colors._width)-1; c>=0; --c) {
27288  const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4+csiz:3;
27289  }
27290  if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
27291  siz+=primitives.size();
27292  cimg::unused(opacities);
27293  return siz;
27294  }
27295 
27297  template<typename tp, typename tc>
27299  const CImgList<tc>& colors) const {
27300  CImgList<T> opacities;
27301  return get_object3dtoCImg3d(primitives,colors,opacities);
27302  }
27303 
27305  template<typename tp>
27307  CImgList<T> colors, opacities;
27308  return get_object3dtoCImg3d(primitives,colors,opacities);
27309  }
27310 
27313  CImgList<T> opacities, colors;
27314  CImgList<uintT> primitives(width(),1,1,1,1);
27315  cimglist_for(primitives,p) primitives(p,0) = p;
27316  return get_object3dtoCImg3d(primitives,colors,opacities);
27317  }
27318 
27320 
27325  template<typename tp, typename tc, typename to>
27326  CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImgList<to>& opacities) {
27327  return get_CImg3dtoobject3d(primitives,colors,opacities).move_to(*this);
27328  }
27329 
27331  template<typename tp, typename tc, typename to>
27332  CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives, CImgList<tc>& colors, CImgList<to>& opacities) const {
27333  char error_message[1024] = { 0 };
27334  if (!is_CImg3d(true,error_message))
27335  throw CImgInstanceException(_cimg_instance
27336  "CImg3dtoobject3d(): image instance is not a CImg3d (%s).",
27337  cimg_instance,error_message);
27338  const T *ptrs = _data + 6;
27339  const unsigned int
27340  nb_points = cimg::float2uint((float)*(ptrs++)),
27341  nb_primitives = cimg::float2uint((float)*(ptrs++));
27342  const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
27343  ptrs+=3*nb_points;
27344  primitives.assign(nb_primitives);
27345  cimglist_for(primitives,p) {
27346  const unsigned int nb_inds = (unsigned int)*(ptrs++);
27347  primitives[p].assign(1,nb_inds);
27348  tp *ptrp = primitives[p]._data;
27349  for (unsigned int i = 0; i<nb_inds; ++i) *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
27350  }
27351  colors.assign(nb_primitives);
27352  cimglist_for(colors,c) {
27353  if (*ptrs==(T)-128) {
27354  ++ptrs;
27355  const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
27356  if (!h && !s) colors[c].assign(colors[w],true);
27357  else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
27358  } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
27359  }
27360  opacities.assign(nb_primitives);
27361  cimglist_for(opacities,o) {
27362  if (*ptrs==(T)-128) {
27363  ++ptrs;
27364  const unsigned int w = (unsigned int)*(ptrs++), h = (unsigned int)*(ptrs++), s = (unsigned int)*(ptrs++);
27365  if (!h && !s) opacities[o].assign(opacities[w],true);
27366  else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=w*h*s; }
27367  } else opacities[o].assign(1,1,1,1,*(ptrs++));
27368  }
27369  return points;
27370  }
27371 
27373  //---------------------------
27374  //
27376 
27377  //---------------------------
27378 
27379  // [internal] The following _draw_scanline() routines are *non user-friendly functions*, used only for internal purpose.
27380  // Pre-requisites: x0<x1, y-coordinate is valid, col is valid.
27381  template<typename tc>
27382  CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
27383  const tc *const color, const float opacity=1,
27384  const float brightness=1, const bool init=false) {
27385  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
27386  static float nopacity = 0, copacity = 0;
27387  static unsigned long whd = 0;
27388  static const tc *col = 0;
27389  if (init) {
27390  nopacity = cimg::abs(opacity);
27391  copacity = 1 - cimg::max(opacity,0);
27392  whd = (unsigned long)_width*_height*_depth;
27393  } else {
27394  const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width()-1, dx = nx1 - nx0;
27395  if (dx>=0) {
27396  col = color;
27397  const unsigned long off = whd - dx - 1;
27398  T *ptrd = data(nx0,y);
27399  if (opacity>=1) { // ** Opaque drawing **
27400  if (brightness==1) { // Brightness==1
27401  if (sizeof(T)!=1) cimg_forC(*this,c) {
27402  const T val = (T)*(col++);
27403  for (int x = dx; x>=0; --x) *(ptrd++) = val;
27404  ptrd+=off;
27405  } else cimg_forC(*this,c) {
27406  const T val = (T)*(col++);
27407  std::memset(ptrd,(int)val,dx+1);
27408  ptrd+=whd;
27409  }
27410  } else if (brightness<1) { // Brightness<1
27411  if (sizeof(T)!=1) cimg_forC(*this,c) {
27412  const T val = (T)(*(col++)*brightness);
27413  for (int x = dx; x>=0; --x) *(ptrd++) = val;
27414  ptrd+=off;
27415  } else cimg_forC(*this,c) {
27416  const T val = (T)(*(col++)*brightness);
27417  std::memset(ptrd,(int)val,dx+1);
27418  ptrd+=whd;
27419  }
27420  } else { // Brightness>1
27421  if (sizeof(T)!=1) cimg_forC(*this,c) {
27422  const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
27423  for (int x = dx; x>=0; --x) *(ptrd++) = val;
27424  ptrd+=off;
27425  } else cimg_forC(*this,c) {
27426  const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
27427  std::memset(ptrd,(int)val,dx+1);
27428  ptrd+=whd;
27429  }
27430  }
27431  } else { // ** Transparent drawing **
27432  if (brightness==1) { // Brightness==1
27433  cimg_forC(*this,c) {
27434  const T val = (T)*(col++);
27435  for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
27436  ptrd+=off;
27437  }
27438  } else if (brightness<=1) { // Brightness<1
27439  cimg_forC(*this,c) {
27440  const T val = (T)(*(col++)*brightness);
27441  for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
27442  ptrd+=off;
27443  }
27444  } else { // Brightness>1
27445  cimg_forC(*this,c) {
27446  const T val = (T)((2-brightness)**(col++) + (brightness-1)*maxval);
27447  for (int x = dx; x>=0; --x) { *ptrd = (T)(val*nopacity + *ptrd*copacity); ++ptrd; }
27448  ptrd+=off;
27449  }
27450  }
27451  }
27452  }
27453  }
27454  return *this;
27455  }
27456 
27457  template<typename tc>
27458  CImg<T>& _draw_scanline(const tc *const color, const float opacity=1) {
27459  return _draw_scanline(0,0,0,color,opacity,0,true);
27460  }
27461 
27463 
27478  template<typename tc>
27479  CImg<T>& draw_point(const int x0, const int y0, const int z0,
27480  const tc *const color, const float opacity=1) {
27481  if (is_empty()) return *this;
27482  if (!color)
27483  throw CImgArgumentException(_cimg_instance
27484  "draw_point(): Specified color is (null).",
27485  cimg_instance);
27486  if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
27487  const unsigned long whd = (unsigned long)_width*_height*_depth;
27488  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27489  T *ptrd = data(x0,y0,z0,0);
27490  const tc *col = color;
27491  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
27492  else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
27493  }
27494  return *this;
27495  }
27496 
27498  template<typename tc>
27499  CImg<T>& draw_point(const int x0, const int y0,
27500  const tc *const color, const float opacity=1) {
27501  return draw_point(x0,y0,0,color,opacity);
27502  }
27503 
27504  // Draw a points cloud.
27510  template<typename t, typename tc>
27511  CImg<T>& draw_point(const CImg<t>& points,
27512  const tc *const color, const float opacity=1) {
27513  if (is_empty() || !points) return *this;
27514  switch (points._height) {
27515  case 0 : case 1 :
27516  throw CImgArgumentException(_cimg_instance
27517  "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).",
27518  cimg_instance,
27519  points._width,points._height,points._depth,points._spectrum,points._data);
27520  case 2 : {
27521  cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
27522  } break;
27523  default : {
27524  cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
27525  }
27526  }
27527  return *this;
27528  }
27529 
27531 
27550  template<typename tc>
27551  CImg<T>& draw_line(const int x0, const int y0,
27552  const int x1, const int y1,
27553  const tc *const color, const float opacity=1,
27554  const unsigned int pattern=~0U, const bool init_hatch=true) {
27555  if (is_empty()) return *this;
27556  if (!color)
27557  throw CImgArgumentException(_cimg_instance
27558  "draw_line(): Specified color is (null).",
27559  cimg_instance);
27560  static unsigned int hatch = ~0U - (~0U>>1);
27561  if (init_hatch) hatch = ~0U - (~0U>>1);
27562  const bool xdir = x0<x1, ydir = y0<y1;
27563  int
27564  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
27565  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
27566  &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
27567  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
27568  &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
27569  if (xright<0 || xleft>=width()) return *this;
27570  if (xleft<0) { yleft-=(int)((float)xleft*((float)yright - yleft)/((float)xright - xleft)); xleft = 0; }
27571  if (xright>=width()) { yright-=(int)(((float)xright - width())*((float)yright - yleft)/((float)xright - xleft)); xright = width() - 1; }
27572  if (ydown<0 || yup>=height()) return *this;
27573  if (yup<0) { xup-=(int)((float)yup*((float)xdown - xup)/((float)ydown - yup)); yup = 0; }
27574  if (ydown>=height()) { xdown-=(int)(((float)ydown - height())*((float)xdown - xup)/((float)ydown - yup)); ydown = height() - 1; }
27575  T *ptrd0 = data(nx0,ny0);
27576  int dx = xright - xleft, dy = ydown - yup;
27577  const bool steep = dy>dx;
27578  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
27579  const long
27580  offx = (nx0<nx1?1:-1)*(steep?width():1),
27581  offy = (ny0<ny1?1:-1)*(steep?1:width());
27582  const unsigned long wh = (unsigned long)_width*_height;
27583  if (opacity>=1) {
27584  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27585  if (pattern&hatch) {
27586  T *ptrd = ptrd0; const tc* col = color;
27587  cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
27588  }
27589  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27590  ptrd0+=offx;
27591  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27592  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27593  T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
27594  ptrd0+=offx;
27595  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27596  }
27597  } else {
27598  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27599  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27600  if (pattern&hatch) {
27601  T *ptrd = ptrd0; const tc* col = color;
27602  cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
27603  }
27604  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27605  ptrd0+=offx;
27606  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27607  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27608  T *ptrd = ptrd0; const tc* col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
27609  ptrd0+=offx;
27610  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27611  }
27612  }
27613  return *this;
27614  }
27615 
27617 
27630  template<typename tz,typename tc>
27632  const int x0, const int y0, const float z0,
27633  const int x1, const int y1, const float z1,
27634  const tc *const color, const float opacity=1,
27635  const unsigned int pattern=~0U, const bool init_hatch=true) {
27636  typedef typename cimg::superset<tz,float>::type tzfloat;
27637  if (is_empty() || z0<=0 || z1<=0) return *this;
27638  if (!color)
27639  throw CImgArgumentException(_cimg_instance
27640  "draw_line(): Specified color is (null).",
27641  cimg_instance);
27642  if (!is_sameXY(zbuffer))
27643  throw CImgArgumentException(_cimg_instance
27644  "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
27645  cimg_instance,
27646  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
27647  static unsigned int hatch = ~0U - (~0U>>1);
27648  if (init_hatch) hatch = ~0U - (~0U>>1);
27649  const bool xdir = x0<x1, ydir = y0<y1;
27650  int
27651  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
27652  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
27653  &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
27654  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
27655  &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
27656  tzfloat
27657  Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1, nz0 = Z0, nz1 = Z1, dz = Z1 - Z0,
27658  &zleft = xdir?nz0:nz1,
27659  &zright = xdir?nz1:nz0,
27660  &zup = ydir?nz0:nz1,
27661  &zdown = ydir?nz1:nz0;
27662  if (xright<0 || xleft>=width()) return *this;
27663  if (xleft<0) {
27664  const float D = (float)xright - xleft;
27665  yleft-=(int)((float)xleft*((float)yright - yleft)/D);
27666  zleft-=(tzfloat)xleft*(zright - zleft)/D;
27667  xleft = 0;
27668  }
27669  if (xright>=width()) {
27670  const float d = (float)xright - width(), D = (float)xright - xleft;
27671  yright-=(int)(d*((float)yright - yleft)/D);
27672  zright-=(tzfloat)d*(zright - zleft)/D;
27673  xright = width() - 1;
27674  }
27675  if (ydown<0 || yup>=height()) return *this;
27676  if (yup<0) {
27677  const float D = (float)ydown - yup;
27678  xup-=(int)((float)yup*((float)xdown - xup)/D);
27679  zup-=(tzfloat)yup*(zdown - zup)/D;
27680  yup = 0;
27681  }
27682  if (ydown>=height()) {
27683  const float d = (float)ydown - height(), D = (float)ydown - yup;
27684  xdown-=(int)(d*((float)xdown - xup)/D);
27685  zdown-=(tzfloat)d*(zdown - zup)/D;
27686  ydown = height() - 1;
27687  }
27688  T *ptrd0 = data(nx0,ny0);
27689  tz *ptrz = zbuffer.data(nx0,ny0);
27690  int dx = xright - xleft, dy = ydown - yup;
27691  const bool steep = dy>dx;
27692  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
27693  const long
27694  offx = (nx0<nx1?1:-1)*(steep?width():1),
27695  offy = (ny0<ny1?1:-1)*(steep?1:width());
27696  const unsigned long wh = (unsigned long)_width*_height,
27697  ndx = dx>0?dx:1;
27698  if (opacity>=1) {
27699  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27700  const tzfloat z = Z0 + x*dz/ndx;
27701  if (z>=(tzfloat)*ptrz && pattern&hatch) {
27702  *ptrz = (tz)z;
27703  T *ptrd = ptrd0; const tc *col = color;
27704  cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
27705  }
27706  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27707  ptrd0+=offx; ptrz+=offx;
27708  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
27709  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27710  const tzfloat z = Z0 + x*dz/ndx;
27711  if (z>=(tzfloat)*ptrz) {
27712  *ptrz = (tz)z;
27713  T *ptrd = ptrd0; const tc *col = color;
27714  cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=wh; }
27715  }
27716  ptrd0+=offx; ptrz+=offx;
27717  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
27718  }
27719  } else {
27720  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27721  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27722  const tzfloat z = Z0 + x*dz/ndx;
27723  if (z>=(tzfloat)*ptrz && pattern&hatch) {
27724  *ptrz = (tz)z;
27725  T *ptrd = ptrd0; const tc *col = color;
27726  cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
27727  }
27728  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27729  ptrd0+=offx; ptrz+=offx;
27730  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
27731  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27732  const tzfloat z = Z0 + x*dz/ndx;
27733  if (z>=(tzfloat)*ptrz) {
27734  *ptrz = (tz)z;
27735  T *ptrd = ptrd0; const tc *col = color;
27736  cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=wh; }
27737  }
27738  ptrd0+=offx; ptrz+=offx;
27739  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
27740  }
27741  }
27742  return *this;
27743  }
27744 
27746 
27758  template<typename tc>
27759  CImg<T>& draw_line(const int x0, const int y0, const int z0,
27760  const int x1, const int y1, const int z1,
27761  const tc *const color, const float opacity=1,
27762  const unsigned int pattern=~0U, const bool init_hatch=true) {
27763  if (is_empty()) return *this;
27764  if (!color)
27765  throw CImgArgumentException(_cimg_instance
27766  "draw_line(): Specified color is (null).",
27767  cimg_instance);
27768  static unsigned int hatch = ~0U - (~0U>>1);
27769  if (init_hatch) hatch = ~0U - (~0U>>1);
27770  int nx0 = x0, ny0 = y0, nz0 = z0, nx1 = x1, ny1 = y1, nz1 = z1;
27771  if (nx0>nx1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
27772  if (nx1<0 || nx0>=width()) return *this;
27773  if (nx0<0) { const float D = 1.0f + nx1 - nx0; ny0-=(int)((float)nx0*(1.0f + ny1 - ny0)/D); nz0-=(int)((float)nx0*(1.0f + nz1 - nz0)/D); nx0 = 0; }
27774  if (nx1>=width()) { const float d = (float)nx1 - width(), D = 1.0f + nx1 - nx0; ny1+=(int)(d*(1.0f + ny0 - ny1)/D); nz1+=(int)(d*(1.0f + nz0 - nz1)/D); nx1 = width() - 1; }
27775  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
27776  if (ny1<0 || ny0>=height()) return *this;
27777  if (ny0<0) { const float D = 1.0f + ny1 - ny0; nx0-=(int)((float)ny0*(1.0f + nx1 - nx0)/D); nz0-=(int)((float)ny0*(1.0f + nz1 - nz0)/D); ny0 = 0; }
27778  if (ny1>=height()) { const float d = (float)ny1 - height(), D = 1.0f + ny1 - ny0; nx1+=(int)(d*(1.0f + nx0 - nx1)/D); nz1+=(int)(d*(1.0f + nz0 - nz1)/D); ny1 = height() - 1; }
27779  if (nz0>nz1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
27780  if (nz1<0 || nz0>=depth()) return *this;
27781  if (nz0<0) { const float D = 1.0f + nz1 - nz0; nx0-=(int)((float)nz0*(1.0f + nx1 - nx0)/D); ny0-=(int)((float)nz0*(1.0f + ny1 - ny0)/D); nz0 = 0; }
27782  if (nz1>=depth()) { const float d = (float)nz1 - depth(), D = 1.0f + nz1 - nz0; nx1+=(int)(d*(1.0f + nx0 - nx1)/D); ny1+=(int)(d*(1.0f + ny0 - ny1)/D); nz1 = depth() - 1; }
27783  const unsigned int dmax = cimg::max(cimg::abs(nx1 - nx0),cimg::abs(ny1 - ny0),nz1 - nz0);
27784  const unsigned long whd = (unsigned long)_width*_height*_depth;
27785  const float px = (nx1 - nx0)/(float)dmax, py = (ny1 - ny0)/(float)dmax, pz = (nz1 - nz0)/(float)dmax;
27786  float x = (float)nx0, y = (float)ny0, z = (float)nz0;
27787  if (opacity>=1) for (unsigned int t = 0; t<=dmax; ++t) {
27788  if (!(~pattern) || (~pattern && pattern&hatch)) {
27789  T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
27790  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
27791  }
27792  x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
27793  } else {
27794  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27795  for (unsigned int t = 0; t<=dmax; ++t) {
27796  if (!(~pattern) || (~pattern && pattern&hatch)) {
27797  T* ptrd = data((unsigned int)x,(unsigned int)y,(unsigned int)z);
27798  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
27799  }
27800  x+=px; y+=py; z+=pz; if (pattern) { hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1); }
27801  }
27802  }
27803  return *this;
27804  }
27805 
27807 
27829  template<typename tc>
27830  CImg<T>& draw_line(const int x0, const int y0,
27831  const int x1, const int y1,
27832  const CImg<tc>& texture,
27833  const int tx0, const int ty0,
27834  const int tx1, const int ty1,
27835  const float opacity=1,
27836  const unsigned int pattern=~0U, const bool init_hatch=true) {
27837  if (is_empty()) return *this;
27838  if (texture._depth>1 || texture._spectrum<_spectrum)
27839  throw CImgArgumentException(_cimg_instance
27840  "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
27841  cimg_instance,
27842  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
27843  if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
27844  static unsigned int hatch = ~0U - (~0U>>1);
27845  if (init_hatch) hatch = ~0U - (~0U>>1);
27846  const bool xdir = x0<x1, ydir = y0<y1;
27847  int
27848  dtx = tx1-tx0, dty = ty1-ty0,
27849  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
27850  tnx0 = tx0, tnx1 = tx1, tny0 = ty0, tny1 = ty1,
27851  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1, &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
27852  &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
27853  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1, &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0,
27854  &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
27855  if (xright<0 || xleft>=width()) return *this;
27856  if (xleft<0) {
27857  const float D = (float)xright - xleft;
27858  yleft-=(int)((float)xleft*((float)yright - yleft)/D);
27859  txleft-=(int)((float)xleft*((float)txright - txleft)/D);
27860  tyleft-=(int)((float)xleft*((float)tyright - tyleft)/D);
27861  xleft = 0;
27862  }
27863  if (xright>=width()) {
27864  const float d = (float)xright - width(), D = (float)xright - xleft;
27865  yright-=(int)(d*((float)yright - yleft)/D);
27866  txright-=(int)(d*((float)txright - txleft)/D);
27867  tyright-=(int)(d*((float)tyright - tyleft)/D);
27868  xright = width() - 1;
27869  }
27870  if (ydown<0 || yup>=height()) return *this;
27871  if (yup<0) {
27872  const float D = (float)ydown - yup;
27873  xup-=(int)((float)yup*((float)xdown - xup)/D);
27874  txup-=(int)((float)yup*((float)txdown - txup)/D);
27875  tyup-=(int)((float)yup*((float)tydown - tyup)/D);
27876  yup = 0;
27877  }
27878  if (ydown>=height()) {
27879  const float d = (float)ydown - height(), D = (float)ydown - yup;
27880  xdown-=(int)(d*((float)xdown - xup)/D);
27881  txdown-=(int)(d*((float)txdown - txup)/D);
27882  tydown-=(int)(d*((float)tydown - tyup)/D);
27883  ydown = height() - 1;
27884  }
27885  T *ptrd0 = data(nx0,ny0);
27886  int dx = xright - xleft, dy = ydown - yup;
27887  const bool steep = dy>dx;
27888  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
27889  const long
27890  offx = (nx0<nx1?1:-1)*(steep?width():1),
27891  offy = (ny0<ny1?1:-1)*(steep?1:width()),
27892  ndx = dx>0?dx:1;
27893  const unsigned long wh = (unsigned long)_width*_height;
27894 
27895  if (opacity>=1) {
27896  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27897  if (pattern&hatch) {
27898  T *ptrd = ptrd0;
27899  const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
27900  cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
27901  }
27902  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27903  ptrd0+=offx;
27904  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27905  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27906  T *ptrd = ptrd0;
27907  const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
27908  cimg_forC(*this,c) { *ptrd = (T)texture(tx,ty,0,c); ptrd+=wh; }
27909  ptrd0+=offx;
27910  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27911  }
27912  } else {
27913  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
27914  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
27915  T *ptrd = ptrd0;
27916  if (pattern&hatch) {
27917  const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
27918  cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
27919  }
27920  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
27921  ptrd0+=offx;
27922  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27923  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
27924  T *ptrd = ptrd0;
27925  const int tx = tx0 + x*dtx/ndx, ty = ty0 + x*dty/ndx;
27926  cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture(tx,ty,0,c) + *ptrd*copacity); ptrd+=wh; }
27927  ptrd0+=offx;
27928  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
27929  }
27930  }
27931  return *this;
27932  }
27933 
27935 
27951  template<typename tc>
27952  CImg<T>& draw_line(const int x0, const int y0, const float z0,
27953  const int x1, const int y1, const float z1,
27954  const CImg<tc>& texture,
27955  const int tx0, const int ty0,
27956  const int tx1, const int ty1,
27957  const float opacity=1,
27958  const unsigned int pattern=~0U, const bool init_hatch=true) {
27959  if (is_empty() && z0<=0 && z1<=0) return *this;
27960  if (texture._depth>1 || texture._spectrum<_spectrum)
27961  throw CImgArgumentException(_cimg_instance
27962  "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
27963  cimg_instance,
27964  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
27965  if (is_overlapped(texture)) return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
27966  static unsigned int hatch = ~0U - (~0U>>1);
27967  if (init_hatch) hatch = ~0U - (~0U>>1);
27968  const bool xdir = x0<x1, ydir = y0<y1;
27969  int
27970  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
27971  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
27972  &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
27973  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
27974  &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
27975  float
27976  Tx0 = tx0/z0, Tx1 = tx1/z1,
27977  Ty0 = ty0/z0, Ty1 = ty1/z1,
27978  Z0 = 1/z0, Z1 = 1/z1,
27979  dz = Z1 - Z0, dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
27980  tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1, nz0 = Z0, nz1 = Z1,
27981  &zleft = xdir?nz0:nz1, &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
27982  &zright = xdir?nz1:nz0, &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
27983  &zup = ydir?nz0:nz1, &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
27984  &zdown = ydir?nz1:nz0, &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
27985  if (xright<0 || xleft>=width()) return *this;
27986  if (xleft<0) {
27987  const float D = (float)xright - xleft;
27988  yleft-=(int)((float)xleft*((float)yright - yleft)/D);
27989  zleft-=(float)xleft*(zright - zleft)/D;
27990  txleft-=(float)xleft*(txright - txleft)/D;
27991  tyleft-=(float)xleft*(tyright - tyleft)/D;
27992  xleft = 0;
27993  }
27994  if (xright>=width()) {
27995  const float d = (float)xright - width(), D = (float)xright - xleft;
27996  yright-=(int)(d*((float)yright - yleft)/D);
27997  zright-=d*(zright - zleft)/D;
27998  txright-=d*(txright - txleft)/D;
27999  tyright-=d*(tyright - tyleft)/D;
28000  xright = width() - 1;
28001  }
28002  if (ydown<0 || yup>=height()) return *this;
28003  if (yup<0) {
28004  const float D = (float)ydown - yup;
28005  xup-=(int)((float)yup*((float)xdown - xup)/D);
28006  zup-=(float)yup*(zdown - zup)/D;
28007  txup-=(float)yup*(txdown - txup)/D;
28008  tyup-=(float)yup*(tydown - tyup)/D;
28009  yup = 0;
28010  }
28011  if (ydown>=height()) {
28012  const float d = (float)ydown - height(), D = (float)ydown - yup;
28013  xdown-=(int)(d*((float)xdown - xup)/D);
28014  zdown-=d*(zdown - zup)/D;
28015  txdown-=d*(txdown - txup)/D;
28016  tydown-=d*(tydown - tyup)/D;
28017  ydown = height() - 1;
28018  }
28019  T *ptrd0 = data(nx0,ny0);
28020  int dx = xright - xleft, dy = ydown - yup;
28021  const bool steep = dy>dx;
28022  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
28023  const long
28024  offx = (nx0<nx1?1:-1)*(steep?width():1),
28025  offy = (ny0<ny1?1:-1)*(steep?1:width()),
28026  ndx = dx>0?dx:1;
28027  const unsigned long wh = (unsigned long)_width*_height;
28028 
28029  if (opacity>=1) {
28030  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
28031  if (pattern&hatch) {
28032  const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28033  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
28034  }
28035  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
28036  ptrd0+=offx;
28037  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
28038  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
28039  const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28040  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
28041  ptrd0+=offx;
28042  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
28043  }
28044  } else {
28045  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28046  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
28047  if (pattern&hatch) {
28048  const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28049  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
28050  }
28051  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
28052  ptrd0+=offx;
28053  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
28054  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
28055  const float z = Z0 + x*dz/ndx, tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28056  T *ptrd = ptrd0;
28057  cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
28058  ptrd0+=offx;
28059  if ((error-=dy)<0) { ptrd0+=offy; error+=dx; }
28060  }
28061  }
28062  return *this;
28063  }
28064 
28066 
28083  template<typename tz, typename tc>
28085  const int x0, const int y0, const float z0,
28086  const int x1, const int y1, const float z1,
28087  const CImg<tc>& texture,
28088  const int tx0, const int ty0,
28089  const int tx1, const int ty1,
28090  const float opacity=1,
28091  const unsigned int pattern=~0U, const bool init_hatch=true) {
28092  typedef typename cimg::superset<tz,float>::type tzfloat;
28093  if (is_empty() || z0<=0 || z1<=0) return *this;
28094  if (!is_sameXY(zbuffer))
28095  throw CImgArgumentException(_cimg_instance
28096  "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
28097  cimg_instance,
28098  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
28099  if (texture._depth>1 || texture._spectrum<_spectrum)
28100  throw CImgArgumentException(_cimg_instance
28101  "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
28102  cimg_instance,
28103  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28104  if (is_overlapped(texture)) return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
28105  static unsigned int hatch = ~0U - (~0U>>1);
28106  if (init_hatch) hatch = ~0U - (~0U>>1);
28107  const bool xdir = x0<x1, ydir = y0<y1;
28108  int
28109  nx0 = x0, nx1 = x1, ny0 = y0, ny1 = y1,
28110  &xleft = xdir?nx0:nx1, &yleft = xdir?ny0:ny1,
28111  &xright = xdir?nx1:nx0, &yright = xdir?ny1:ny0,
28112  &xup = ydir?nx0:nx1, &yup = ydir?ny0:ny1,
28113  &xdown = ydir?nx1:nx0, &ydown = ydir?ny1:ny0;
28114  float
28115  Tx0 = tx0/z0, Tx1 = tx1/z1,
28116  Ty0 = ty0/z0, Ty1 = ty1/z1,
28117  dtx = Tx1 - Tx0, dty = Ty1 - Ty0,
28118  tnx0 = Tx0, tnx1 = Tx1, tny0 = Ty0, tny1 = Ty1,
28119  &txleft = xdir?tnx0:tnx1, &tyleft = xdir?tny0:tny1,
28120  &txright = xdir?tnx1:tnx0, &tyright = xdir?tny1:tny0,
28121  &txup = ydir?tnx0:tnx1, &tyup = ydir?tny0:tny1,
28122  &txdown = ydir?tnx1:tnx0, &tydown = ydir?tny1:tny0;
28123  tzfloat
28124  Z0 = 1/(tzfloat)z0, Z1 = 1/(tzfloat)z1,
28125  dz = Z1 - Z0, nz0 = Z0, nz1 = Z1,
28126  &zleft = xdir?nz0:nz1,
28127  &zright = xdir?nz1:nz0,
28128  &zup = ydir?nz0:nz1,
28129  &zdown = ydir?nz1:nz0;
28130  if (xright<0 || xleft>=width()) return *this;
28131  if (xleft<0) {
28132  const float D = (float)xright - xleft;
28133  yleft-=(int)((float)xleft*((float)yright - yleft)/D);
28134  zleft-=(float)xleft*(zright - zleft)/D;
28135  txleft-=(float)xleft*(txright - txleft)/D;
28136  tyleft-=(float)xleft*(tyright - tyleft)/D;
28137  xleft = 0;
28138  }
28139  if (xright>=width()) {
28140  const float d = (float)xright - width(), D = (float)xright - xleft;
28141  yright-=(int)(d*((float)yright - yleft)/D);
28142  zright-=d*(zright - zleft)/D;
28143  txright-=d*(txright - txleft)/D;
28144  tyright-=d*(tyright - tyleft)/D;
28145  xright = width()-1;
28146  }
28147  if (ydown<0 || yup>=height()) return *this;
28148  if (yup<0) {
28149  const float D = (float)ydown - yup;
28150  xup-=(int)((float)yup*((float)xdown - xup)/D);
28151  zup-=yup*(zdown - zup)/D;
28152  txup-=yup*(txdown - txup)/D;
28153  tyup-=yup*(tydown - tyup)/D;
28154  yup = 0;
28155  }
28156  if (ydown>=height()) {
28157  const float d = (float)ydown - height(), D = (float)ydown - yup;
28158  xdown-=(int)(d*((float)xdown - xup)/D);
28159  zdown-=d*(zdown - zup)/D;
28160  txdown-=d*(txdown - txup)/D;
28161  tydown-=d*(tydown - tyup)/D;
28162  ydown = height()-1;
28163  }
28164  T *ptrd0 = data(nx0,ny0);
28165  tz *ptrz = zbuffer.data(nx0,ny0);
28166  int dx = xright - xleft, dy = ydown - yup;
28167  const bool steep = dy>dx;
28168  if (steep) cimg::swap(nx0,ny0,nx1,ny1,dx,dy);
28169  const long
28170  offx = (nx0<nx1?1:-1)*(steep?width():1),
28171  offy = (ny0<ny1?1:-1)*(steep?1:width()),
28172  ndx = dx>0?dx:1;
28173  const unsigned long wh = (unsigned long)_width*_height;
28174 
28175  if (opacity>=1) {
28176  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
28177  if (pattern&hatch) {
28178  const tzfloat z = Z0 + x*dz/ndx;
28179  if (z>=(tzfloat)*ptrz) {
28180  *ptrz = (tz)z;
28181  const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28182  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
28183  }
28184  }
28185  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
28186  ptrd0+=offx; ptrz+=offx;
28187  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
28188  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
28189  const tzfloat z = Z0 + x*dz/ndx;
28190  if (z>=(tzfloat)*ptrz) {
28191  *ptrz = (tz)z;
28192  const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28193  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)texture((int)(tx/z),(int)(ty/z),0,c); ptrd+=wh; }
28194  }
28195  ptrd0+=offx; ptrz+=offx;
28196  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
28197  }
28198  } else {
28199  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
28200  if (~pattern) for (int error = dx>>1, x = 0; x<=dx; ++x) {
28201  if (pattern&hatch) {
28202  const tzfloat z = Z0 + x*dz/ndx;
28203  if (z>=(tzfloat)*ptrz) {
28204  *ptrz = (tz)z;
28205  const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28206  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
28207  }
28208  }
28209  hatch>>=1; if (!hatch) hatch = ~0U - (~0U>>1);
28210  ptrd0+=offx; ptrz+=offx;
28211  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offy; error+=dx; }
28212  } else for (int error = dx>>1, x = 0; x<=dx; ++x) {
28213  const tzfloat z = Z0 + x*dz/ndx;
28214  if (z>=(tzfloat)*ptrz) {
28215  *ptrz = (tz)z;
28216  const float tx = Tx0 + x*dtx/ndx, ty = Ty0 + x*dty/ndx;
28217  T *ptrd = ptrd0; cimg_forC(*this,c) { *ptrd = (T)(nopacity*texture((int)(tx/z),(int)(ty/z),0,c) + *ptrd*copacity); ptrd+=wh; }
28218  }
28219  ptrd0+=offx; ptrz+=offx;
28220  if ((error-=dy)<0) { ptrd0+=offy; ptrz+=offx; error+=dx; }
28221  }
28222  }
28223  return *this;
28224  }
28225 
28227 
28248  template<typename t, typename tc>
28249  CImg<T>& draw_line(const CImg<t>& points,
28250  const tc *const color, const float opacity=1,
28251  const unsigned int pattern=~0U, const bool init_hatch=true) {
28252  if (is_empty() || !points || points._width<2) return *this;
28253  bool ninit_hatch = init_hatch;
28254  switch (points._height) {
28255  case 0 : case 1 :
28256  throw CImgArgumentException(_cimg_instance
28257  "draw_line(): Invalid specified point set (%u,%u,%u,%u,%p).",
28258  cimg_instance,
28259  points._width,points._height,points._depth,points._spectrum,points._data);
28260 
28261  case 2 : {
28262  const int x0 = (int)points(0,0), y0 = (int)points(0,1);
28263  int ox = x0, oy = y0;
28264  for (unsigned int i = 1; i<points._width; ++i) {
28265  const int x = (int)points(i,0), y = (int)points(i,1);
28266  draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
28267  ninit_hatch = false;
28268  ox = x; oy = y;
28269  }
28270  } break;
28271  default : {
28272  const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
28273  int ox = x0, oy = y0, oz = z0;
28274  for (unsigned int i = 1; i<points._width; ++i) {
28275  const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
28276  draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
28277  ninit_hatch = false;
28278  ox = x; oy = y; oz = z;
28279  }
28280  }
28281  }
28282  return *this;
28283  }
28284 
28286 
28297  template<typename tc>
28298  CImg<T>& draw_arrow(const int x0, const int y0,
28299  const int x1, const int y1,
28300  const tc *const color, const float opacity=1,
28301  const float angle=30, const float length=-10,
28302  const unsigned int pattern=~0U) {
28303  if (is_empty()) return *this;
28304  const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
28305  deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.0f,
28306  l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
28307  if (sq>0) {
28308  const float
28309  cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
28310  cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
28311  const int
28312  xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
28313  xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
28314  xc = x1 + (int)((l+1)*(cl+cr))/2, yc = y1 + (int)((l+1)*(sl+sr))/2;
28315  draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
28316  } else draw_point(x0,y0,color,opacity);
28317  return *this;
28318  }
28319 
28321 
28352  template<typename tc>
28353  CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
28354  const int x1, const int y1, const float u1, const float v1,
28355  const tc *const color, const float opacity=1,
28356  const float precision=0.25, const unsigned int pattern=~0U,
28357  const bool init_hatch=true) {
28358  if (is_empty()) return *this;
28359  if (!color)
28360  throw CImgArgumentException(_cimg_instance
28361  "draw_spline(): Specified color is (null).",
28362  cimg_instance);
28363  if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
28364  bool ninit_hatch = init_hatch;
28365  const float
28366  ax = u0 + u1 + 2*(x0 - x1),
28367  bx = 3*(x1 - x0) - 2*u0 - u1,
28368  ay = v0 + v1 + 2*(y0 - y1),
28369  by = 3*(y1 - y0) - 2*v0 - v1,
28370  _precision = 1/(std::sqrt(cimg::sqr((float)x0-x1)+cimg::sqr((float)y0-y1))*(precision>0?precision:1));
28371  int ox = x0, oy = y0;
28372  for (float t = 0; t<1; t+=_precision) {
28373  const float t2 = t*t, t3 = t2*t;
28374  const int
28375  nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
28376  ny = (int)(ay*t3 + by*t2 + v0*t + y0);
28377  draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
28378  ninit_hatch = false;
28379  ox = nx; oy = ny;
28380  }
28381  return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
28382  }
28383 
28385 
28389  template<typename tc>
28390  CImg<T>& draw_spline(const int x0, const int y0, const int z0, const float u0, const float v0, const float w0,
28391  const int x1, const int y1, const int z1, const float u1, const float v1, const float w1,
28392  const tc *const color, const float opacity=1,
28393  const float precision=4, const unsigned int pattern=~0U,
28394  const bool init_hatch=true) {
28395  if (is_empty()) return *this;
28396  if (!color)
28397  throw CImgArgumentException(_cimg_instance
28398  "draw_spline(): Specified color is (null).",
28399  cimg_instance);
28400  if (x0==x1 && y0==y1 && z0==z1) return draw_point(x0,y0,z0,color,opacity);
28401  bool ninit_hatch = init_hatch;
28402  const float
28403  ax = u0 + u1 + 2*(x0 - x1),
28404  bx = 3*(x1 - x0) - 2*u0 - u1,
28405  ay = v0 + v1 + 2*(y0 - y1),
28406  by = 3*(y1 - y0) - 2*v0 - v1,
28407  az = w0 + w1 + 2*(z0 - z1),
28408  bz = 3*(z1 - z0) - 2*w0 - w1,
28409  _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1));
28410  int ox = x0, oy = y0, oz = z0;
28411  for (float t = 0; t<1; t+=_precision) {
28412  const float t2 = t*t, t3 = t2*t;
28413  const int
28414  nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
28415  ny = (int)(ay*t3 + by*t2 + v0*t + y0),
28416  nz = (int)(az*t3 + bz*t2 + w0*t + z0);
28417  draw_line(ox,oy,oz,nx,ny,nz,color,opacity,pattern,ninit_hatch);
28418  ninit_hatch = false;
28419  ox = nx; oy = ny; oz = nz;
28420  }
28421  return draw_line(ox,oy,oz,x1,y1,z1,color,opacity,pattern,false);
28422  }
28423 
28425 
28444  template<typename t>
28445  CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
28446  const int x1, const int y1, const float u1, const float v1,
28447  const CImg<t>& texture,
28448  const int tx0, const int ty0, const int tx1, const int ty1,
28449  const float opacity=1,
28450  const float precision=4, const unsigned int pattern=~0U,
28451  const bool init_hatch=true) {
28452  if (texture._depth>1 || texture._spectrum<_spectrum)
28453  throw CImgArgumentException(_cimg_instance
28454  "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).",
28455  cimg_instance,
28456  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
28457  if (is_empty()) return *this;
28458  if (is_overlapped(texture)) return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
28459  if (x0==x1 && y0==y1) return draw_point(x0,y0,texture.get_vector_at(x0,y0),opacity);
28460  bool ninit_hatch = init_hatch;
28461  const float
28462  ax = u0 + u1 + 2*(x0 - x1),
28463  bx = 3*(x1 - x0) - 2*u0 - u1,
28464  ay = v0 + v1 + 2*(y0 - y1),
28465  by = 3*(y1 - y0) - 2*v0 - v1,
28466  _precision = 1/(std::sqrt(cimg::sqr(x0-x1)+cimg::sqr(y0-y1))*(precision>0?precision:1));
28467  int ox = x0, oy = y0, otx = tx0, oty = ty0;
28468  for (float t1 = 0; t1<1; t1+=_precision) {
28469  const float t2 = t1*t1, t3 = t2*t1;
28470  const int
28471  nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
28472  ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
28473  ntx = tx0 + (int)((tx1-tx0)*t1),
28474  nty = ty0 + (int)((ty1-ty0)*t1);
28475  draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
28476  ninit_hatch = false;
28477  ox = nx; oy = ny; otx = ntx; oty = nty;
28478  }
28479  return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
28480  }
28481 
28483 
28493  template<typename tp, typename tt, typename tc>
28494  CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
28495  const tc *const color, const float opacity=1,
28496  const bool is_closed_set=false, const float precision=4,
28497  const unsigned int pattern=~0U, const bool init_hatch=true) {
28498  if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
28499  bool ninit_hatch = init_hatch;
28500  switch (points._height) {
28501  case 0 : case 1 :
28502  throw CImgArgumentException(_cimg_instance
28503  "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
28504  cimg_instance,
28505  points._width,points._height,points._depth,points._spectrum,points._data);
28506 
28507  case 2 : {
28508  const int x0 = (int)points(0,0), y0 = (int)points(0,1);
28509  const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
28510  int ox = x0, oy = y0;
28511  float ou = u0, ov = v0;
28512  for (unsigned int i = 1; i<points._width; ++i) {
28513  const int x = (int)points(i,0), y = (int)points(i,1);
28514  const float u = (float)tangents(i,0), v = (float)tangents(i,1);
28515  draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
28516  ninit_hatch = false;
28517  ox = x; oy = y; ou = u; ov = v;
28518  }
28519  if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
28520  } break;
28521  default : {
28522  const int x0 = (int)points(0,0), y0 = (int)points(0,1), z0 = (int)points(0,2);
28523  const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1), w0 = (float)tangents(0,2);
28524  int ox = x0, oy = y0, oz = z0;
28525  float ou = u0, ov = v0, ow = w0;
28526  for (unsigned int i = 1; i<points._width; ++i) {
28527  const int x = (int)points(i,0), y = (int)points(i,1), z = (int)points(i,2);
28528  const float u = (float)tangents(i,0), v = (float)tangents(i,1), w = (float)tangents(i,2);
28529  draw_spline(ox,oy,oz,ou,ov,ow,x,y,z,u,v,w,color,opacity,pattern,ninit_hatch);
28530  ninit_hatch = false;
28531  ox = x; oy = y; oz = z; ou = u; ov = v; ow = w;
28532  }
28533  if (is_closed_set) draw_spline(ox,oy,oz,ou,ov,ow,x0,y0,z0,u0,v0,w0,color,precision,opacity,pattern,false);
28534  }
28535  }
28536  return *this;
28537  }
28538 
28540 
28543  template<typename tp, typename tc>
28544  CImg<T>& draw_spline(const CImg<tp>& points,
28545  const tc *const color, const float opacity=1,
28546  const bool is_closed_set=false, const float precision=4,
28547  const unsigned int pattern=~0U, const bool init_hatch=true) {
28548  if (is_empty() || !points || points._width<2) return *this;
28549  CImg<Tfloat> tangents;
28550  switch (points._height) {
28551  case 0 : case 1 :
28552  throw CImgArgumentException(_cimg_instance
28553  "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
28554  cimg_instance,
28555  points._width,points._height,points._depth,points._spectrum,points._data);
28556  case 2 : {
28557  tangents.assign(points._width,points._height);
28558  cimg_forX(points,p) {
28559  const unsigned int
28560  p0 = is_closed_set?(p+points._width-1)%points._width:(p?p-1:0),
28561  p1 = is_closed_set?(p+1)%points._width:(p+1<points._width?p+1:p);
28562  const float
28563  x = (float)points(p,0),
28564  y = (float)points(p,1),
28565  x0 = (float)points(p0,0),
28566  y0 = (float)points(p0,1),
28567  x1 = (float)points(p1,0),
28568  y1 = (float)points(p1,1),
28569  u0 = x - x0,
28570  v0 = y - y0,
28571  n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0),
28572  u1 = x1 - x,
28573  v1 = y1 - y,
28574  n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1),
28575  u = u0/n0 + u1/n1,
28576  v = v0/n0 + v1/n1,
28577  n = 1e-8f + (float)std::sqrt(u*u + v*v),
28578  fact = 0.5f*(n0 + n1);
28579  tangents(p,0) = (Tfloat)(fact*u/n);
28580  tangents(p,1) = (Tfloat)(fact*v/n);
28581  }
28582  } break;
28583  default : {
28584  tangents.assign(points._width,points._height);
28585  cimg_forX(points,p) {
28586  const unsigned int
28587  p0 = is_closed_set?(p+points._width-1)%points._width:(p?p-1:0),
28588  p1 = is_closed_set?(p+1)%points._width:(p+1<points._width?p+1:p);
28589  const float
28590  x = (float)points(p,0),
28591  y = (float)points(p,1),
28592  z = (float)points(p,2),
28593  x0 = (float)points(p0,0),
28594  y0 = (float)points(p0,1),
28595  z0 = (float)points(p0,2),
28596  x1 = (float)points(p1,0),
28597  y1 = (float)points(p1,1),
28598  z1 = (float)points(p1,2),
28599  u0 = x - x0,
28600  v0 = y - y0,
28601  w0 = z - z0,
28602  n0 = 1e-8f + (float)std::sqrt(u0*u0 + v0*v0 + w0*w0),
28603  u1 = x1 - x,
28604  v1 = y1 - y,
28605  w1 = z1 - z,
28606  n1 = 1e-8f + (float)std::sqrt(u1*u1 + v1*v1 + w1*w1),
28607  u = u0/n0 + u1/n1,
28608  v = v0/n0 + v1/n1,
28609  w = w0/n0 + w1/n1,
28610  n = 1e-8f + (float)std::sqrt(u*u + v*v + w*w),
28611  fact = 0.5f*(n0 + n1);
28612  tangents(p,0) = (Tfloat)(fact*u/n);
28613  tangents(p,1) = (Tfloat)(fact*v/n);
28614  tangents(p,2) = (Tfloat)(fact*w/n);
28615  }
28616  }
28617  }
28618  return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
28619  }
28620 
28621  // Inner macro for drawing triangles.
28622 #define _cimg_for_triangle1(img,xl,xr,y,x0,y0,x1,y1,x2,y2) \
28623  for (int y = y0<0?0:y0, \
28624  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28625  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28626  _sxn=1, \
28627  _sxr=1, \
28628  _sxl=1, \
28629  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
28630  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
28631  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
28632  _dyn = y2-y1, \
28633  _dyr = y2-y0, \
28634  _dyl = y1-y0, \
28635  _counter = (_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28636  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28637  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28638  cimg::min((int)(img)._height-y-1,y2-y)), \
28639  _errn = _dyn/2, \
28640  _errr = _dyr/2, \
28641  _errl = _dyl/2, \
28642  _rxn = _dyn?(x2-x1)/_dyn:0, \
28643  _rxr = _dyr?(x2-x0)/_dyr:0, \
28644  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28645  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn); \
28646  _counter>=0; --_counter, ++y, \
28647  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28648  xl+=(y!=y1)?_rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0): \
28649  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28650 
28651 #define _cimg_for_triangle2(img,xl,cl,xr,cr,y,x0,y0,c0,x1,y1,c1,x2,y2,c2) \
28652  for (int y = y0<0?0:y0, \
28653  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28654  cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
28655  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28656  cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
28657  _sxn=1, _scn=1, \
28658  _sxr=1, _scr=1, \
28659  _sxl=1, _scl=1, \
28660  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
28661  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
28662  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
28663  _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
28664  _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
28665  _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
28666  _dyn = y2-y1, \
28667  _dyr = y2-y0, \
28668  _dyl = y1-y0, \
28669  _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28670  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28671  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28672  _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
28673  _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
28674  _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
28675  cimg::min((int)(img)._height-y-1,y2-y)), \
28676  _errn = _dyn/2, _errcn = _errn, \
28677  _errr = _dyr/2, _errcr = _errr, \
28678  _errl = _dyl/2, _errcl = _errl, \
28679  _rxn = _dyn?(x2-x1)/_dyn:0, \
28680  _rcn = _dyn?(c2-c1)/_dyn:0, \
28681  _rxr = _dyr?(x2-x0)/_dyr:0, \
28682  _rcr = _dyr?(c2-c0)/_dyr:0, \
28683  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28684  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
28685  _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
28686  (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ); \
28687  _counter>=0; --_counter, ++y, \
28688  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28689  cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
28690  xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
28691  _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
28692  (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
28693  _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28694 
28695 #define _cimg_for_triangle3(img,xl,txl,tyl,xr,txr,tyr,y,x0,y0,tx0,ty0,x1,y1,tx1,ty1,x2,y2,tx2,ty2) \
28696  for (int y = y0<0?0:y0, \
28697  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28698  txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
28699  tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
28700  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28701  txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
28702  tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
28703  _sxn=1, _stxn=1, _styn=1, \
28704  _sxr=1, _stxr=1, _styr=1, \
28705  _sxl=1, _stxl=1, _styl=1, \
28706  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
28707  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
28708  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
28709  _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
28710  _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
28711  _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
28712  _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
28713  _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
28714  _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
28715  _dyn = y2-y1, \
28716  _dyr = y2-y0, \
28717  _dyl = y1-y0, \
28718  _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28719  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28720  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28721  _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
28722  _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
28723  _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
28724  _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
28725  _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
28726  _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
28727  cimg::min((int)(img)._height-y-1,y2-y)), \
28728  _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, \
28729  _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, \
28730  _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, \
28731  _rxn = _dyn?(x2-x1)/_dyn:0, \
28732  _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
28733  _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
28734  _rxr = _dyr?(x2-x0)/_dyr:0, \
28735  _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
28736  _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
28737  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28738  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
28739  _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
28740  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
28741  _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
28742  (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
28743  _counter>=0; --_counter, ++y, \
28744  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28745  txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
28746  tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
28747  xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
28748  tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
28749  _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
28750  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
28751  _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1,\
28752  _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28753 
28754 #define _cimg_for_triangle4(img,xl,cl,txl,tyl,xr,cr,txr,tyr,y,x0,y0,c0,tx0,ty0,x1,y1,c1,tx1,ty1,x2,y2,c2,tx2,ty2) \
28755  for (int y = y0<0?0:y0, \
28756  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28757  cr = y0>=0?c0:(c0-y0*(c2-c0)/(y2-y0)), \
28758  txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
28759  tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
28760  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28761  cl = y1>=0?(y0>=0?(y0==y1?c1:c0):(c0-y0*(c1-c0)/(y1-y0))):(c1-y1*(c2-c1)/(y2-y1)), \
28762  txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
28763  tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
28764  _sxn=1, _scn=1, _stxn=1, _styn=1, \
28765  _sxr=1, _scr=1, _stxr=1, _styr=1, \
28766  _sxl=1, _scl=1, _stxl=1, _styl=1, \
28767  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), \
28768  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), \
28769  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), \
28770  _dcn = c2>c1?c2-c1:(_scn=-1,c1-c2), \
28771  _dcr = c2>c0?c2-c0:(_scr=-1,c0-c2), \
28772  _dcl = c1>c0?c1-c0:(_scl=-1,c0-c1), \
28773  _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
28774  _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
28775  _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
28776  _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
28777  _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
28778  _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
28779  _dyn = y2-y1, \
28780  _dyr = y2-y0, \
28781  _dyl = y1-y0, \
28782  _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28783  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28784  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28785  _dcn-=_dyn?_dyn*(_dcn/_dyn):0, \
28786  _dcr-=_dyr?_dyr*(_dcr/_dyr):0, \
28787  _dcl-=_dyl?_dyl*(_dcl/_dyl):0, \
28788  _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
28789  _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
28790  _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
28791  _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
28792  _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
28793  _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
28794  cimg::min((int)(img)._height-y-1,y2-y)), \
28795  _errn = _dyn/2, _errcn = _errn, _errtxn = _errn, _errtyn = _errn, \
28796  _errr = _dyr/2, _errcr = _errr, _errtxr = _errr, _errtyr = _errr, \
28797  _errl = _dyl/2, _errcl = _errl, _errtxl = _errl, _errtyl = _errl, \
28798  _rxn = _dyn?(x2-x1)/_dyn:0, \
28799  _rcn = _dyn?(c2-c1)/_dyn:0, \
28800  _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
28801  _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
28802  _rxr = _dyr?(x2-x0)/_dyr:0, \
28803  _rcr = _dyr?(c2-c0)/_dyr:0, \
28804  _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
28805  _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
28806  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28807  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
28808  _rcl = (y0!=y1 && y1>0)?(_dyl?(c1-c0)/_dyl:0): \
28809  (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcn ), \
28810  _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
28811  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
28812  _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
28813  (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ); \
28814  _counter>=0; --_counter, ++y, \
28815  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28816  cr+=_rcr+((_errcr-=_dcr)<0?_errcr+=_dyr,_scr:0), \
28817  txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
28818  tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
28819  xl+=(y!=y1)?(cl+=_rcl+((_errcl-=_dcl)<0?(_errcl+=_dyl,_scl):0), \
28820  txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
28821  tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
28822  _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
28823  (_errcl=_errcn, _dcl=_dcn, _dyl=_dyn, _scl=_scn, _rcl=_rcn, cl=c1, \
28824  _errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
28825  _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
28826  _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28827 
28828 #define _cimg_for_triangle5(img,xl,txl,tyl,lxl,lyl,xr,txr,tyr,lxr,lyr,y,x0,y0,tx0,ty0,lx0,ly0,x1,y1,tx1,ty1,lx1,ly1,x2,y2,tx2,ty2,lx2,ly2) \
28829  for (int y = y0<0?0:y0, \
28830  xr = y0>=0?x0:(x0-y0*(x2-x0)/(y2-y0)), \
28831  txr = y0>=0?tx0:(tx0-y0*(tx2-tx0)/(y2-y0)), \
28832  tyr = y0>=0?ty0:(ty0-y0*(ty2-ty0)/(y2-y0)), \
28833  lxr = y0>=0?lx0:(lx0-y0*(lx2-lx0)/(y2-y0)), \
28834  lyr = y0>=0?ly0:(ly0-y0*(ly2-ly0)/(y2-y0)), \
28835  xl = y1>=0?(y0>=0?(y0==y1?x1:x0):(x0-y0*(x1-x0)/(y1-y0))):(x1-y1*(x2-x1)/(y2-y1)), \
28836  txl = y1>=0?(y0>=0?(y0==y1?tx1:tx0):(tx0-y0*(tx1-tx0)/(y1-y0))):(tx1-y1*(tx2-tx1)/(y2-y1)), \
28837  tyl = y1>=0?(y0>=0?(y0==y1?ty1:ty0):(ty0-y0*(ty1-ty0)/(y1-y0))):(ty1-y1*(ty2-ty1)/(y2-y1)), \
28838  lxl = y1>=0?(y0>=0?(y0==y1?lx1:lx0):(lx0-y0*(lx1-lx0)/(y1-y0))):(lx1-y1*(lx2-lx1)/(y2-y1)), \
28839  lyl = y1>=0?(y0>=0?(y0==y1?ly1:ly0):(ly0-y0*(ly1-ly0)/(y1-y0))):(ly1-y1*(ly2-ly1)/(y2-y1)), \
28840  _sxn=1, _stxn=1, _styn=1, _slxn=1, _slyn=1, \
28841  _sxr=1, _stxr=1, _styr=1, _slxr=1, _slyr=1, \
28842  _sxl=1, _stxl=1, _styl=1, _slxl=1, _slyl=1, \
28843  _dxn = x2>x1?x2-x1:(_sxn=-1,x1-x2), _dyn = y2-y1, \
28844  _dxr = x2>x0?x2-x0:(_sxr=-1,x0-x2), _dyr = y2-y0, \
28845  _dxl = x1>x0?x1-x0:(_sxl=-1,x0-x1), _dyl = y1-y0, \
28846  _dtxn = tx2>tx1?tx2-tx1:(_stxn=-1,tx1-tx2), \
28847  _dtxr = tx2>tx0?tx2-tx0:(_stxr=-1,tx0-tx2), \
28848  _dtxl = tx1>tx0?tx1-tx0:(_stxl=-1,tx0-tx1), \
28849  _dtyn = ty2>ty1?ty2-ty1:(_styn=-1,ty1-ty2), \
28850  _dtyr = ty2>ty0?ty2-ty0:(_styr=-1,ty0-ty2), \
28851  _dtyl = ty1>ty0?ty1-ty0:(_styl=-1,ty0-ty1), \
28852  _dlxn = lx2>lx1?lx2-lx1:(_slxn=-1,lx1-lx2), \
28853  _dlxr = lx2>lx0?lx2-lx0:(_slxr=-1,lx0-lx2), \
28854  _dlxl = lx1>lx0?lx1-lx0:(_slxl=-1,lx0-lx1), \
28855  _dlyn = ly2>ly1?ly2-ly1:(_slyn=-1,ly1-ly2), \
28856  _dlyr = ly2>ly0?ly2-ly0:(_slyr=-1,ly0-ly2), \
28857  _dlyl = ly1>ly0?ly1-ly0:(_slyl=-1,ly0-ly1), \
28858  _counter =(_dxn-=_dyn?_dyn*(_dxn/_dyn):0, \
28859  _dxr-=_dyr?_dyr*(_dxr/_dyr):0, \
28860  _dxl-=_dyl?_dyl*(_dxl/_dyl):0, \
28861  _dtxn-=_dyn?_dyn*(_dtxn/_dyn):0, \
28862  _dtxr-=_dyr?_dyr*(_dtxr/_dyr):0, \
28863  _dtxl-=_dyl?_dyl*(_dtxl/_dyl):0, \
28864  _dtyn-=_dyn?_dyn*(_dtyn/_dyn):0, \
28865  _dtyr-=_dyr?_dyr*(_dtyr/_dyr):0, \
28866  _dtyl-=_dyl?_dyl*(_dtyl/_dyl):0, \
28867  _dlxn-=_dyn?_dyn*(_dlxn/_dyn):0, \
28868  _dlxr-=_dyr?_dyr*(_dlxr/_dyr):0, \
28869  _dlxl-=_dyl?_dyl*(_dlxl/_dyl):0, \
28870  _dlyn-=_dyn?_dyn*(_dlyn/_dyn):0, \
28871  _dlyr-=_dyr?_dyr*(_dlyr/_dyr):0, \
28872  _dlyl-=_dyl?_dyl*(_dlyl/_dyl):0, \
28873  cimg::min((int)(img)._height-y-1,y2-y)), \
28874  _errn = _dyn/2, _errtxn = _errn, _errtyn = _errn, _errlxn = _errn, _errlyn = _errn, \
28875  _errr = _dyr/2, _errtxr = _errr, _errtyr = _errr, _errlxr = _errr, _errlyr = _errr, \
28876  _errl = _dyl/2, _errtxl = _errl, _errtyl = _errl, _errlxl = _errl, _errlyl = _errl, \
28877  _rxn = _dyn?(x2-x1)/_dyn:0, \
28878  _rtxn = _dyn?(tx2-tx1)/_dyn:0, \
28879  _rtyn = _dyn?(ty2-ty1)/_dyn:0, \
28880  _rlxn = _dyn?(lx2-lx1)/_dyn:0, \
28881  _rlyn = _dyn?(ly2-ly1)/_dyn:0, \
28882  _rxr = _dyr?(x2-x0)/_dyr:0, \
28883  _rtxr = _dyr?(tx2-tx0)/_dyr:0, \
28884  _rtyr = _dyr?(ty2-ty0)/_dyr:0, \
28885  _rlxr = _dyr?(lx2-lx0)/_dyr:0, \
28886  _rlyr = _dyr?(ly2-ly0)/_dyr:0, \
28887  _rxl = (y0!=y1 && y1>0)?(_dyl?(x1-x0)/_dyl:0): \
28888  (_errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxn), \
28889  _rtxl = (y0!=y1 && y1>0)?(_dyl?(tx1-tx0)/_dyl:0): \
28890  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxn ), \
28891  _rtyl = (y0!=y1 && y1>0)?(_dyl?(ty1-ty0)/_dyl:0): \
28892  (_errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyn ), \
28893  _rlxl = (y0!=y1 && y1>0)?(_dyl?(lx1-lx0)/_dyl:0): \
28894  (_errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxn ), \
28895  _rlyl = (y0!=y1 && y1>0)?(_dyl?(ly1-ly0)/_dyl:0): \
28896  (_errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyn ); \
28897  _counter>=0; --_counter, ++y, \
28898  xr+=_rxr+((_errr-=_dxr)<0?_errr+=_dyr,_sxr:0), \
28899  txr+=_rtxr+((_errtxr-=_dtxr)<0?_errtxr+=_dyr,_stxr:0), \
28900  tyr+=_rtyr+((_errtyr-=_dtyr)<0?_errtyr+=_dyr,_styr:0), \
28901  lxr+=_rlxr+((_errlxr-=_dlxr)<0?_errlxr+=_dyr,_slxr:0), \
28902  lyr+=_rlyr+((_errlyr-=_dlyr)<0?_errlyr+=_dyr,_slyr:0), \
28903  xl+=(y!=y1)?(txl+=_rtxl+((_errtxl-=_dtxl)<0?(_errtxl+=_dyl,_stxl):0), \
28904  tyl+=_rtyl+((_errtyl-=_dtyl)<0?(_errtyl+=_dyl,_styl):0), \
28905  lxl+=_rlxl+((_errlxl-=_dlxl)<0?(_errlxl+=_dyl,_slxl):0), \
28906  lyl+=_rlyl+((_errlyl-=_dlyl)<0?(_errlyl+=_dyl,_slyl):0), \
28907  _rxl+((_errl-=_dxl)<0?(_errl+=_dyl,_sxl):0)): \
28908  (_errtxl=_errtxn, _dtxl=_dtxn, _dyl=_dyn, _stxl=_stxn, _rtxl=_rtxn, txl=tx1, \
28909  _errtyl=_errtyn, _dtyl=_dtyn, _dyl=_dyn, _styl=_styn, _rtyl=_rtyn, tyl=ty1, \
28910  _errlxl=_errlxn, _dlxl=_dlxn, _dyl=_dyn, _slxl=_slxn, _rlxl=_rlxn, lxl=lx1, \
28911  _errlyl=_errlyn, _dlyl=_dlyn, _dyl=_dyn, _slyl=_slyn, _rlyl=_rlyn, lyl=ly1, \
28912  _errl=_errn, _dxl=_dxn, _dyl=_dyn, _sxl=_sxn, _rxl=_rxn, x1-xl))
28913 
28914  // [internal] Draw a filled triangle.
28915  template<typename tc>
28916  CImg<T>& _draw_triangle(const int x0, const int y0,
28917  const int x1, const int y1,
28918  const int x2, const int y2,
28919  const tc *const color, const float opacity,
28920  const float brightness) {
28921  _draw_scanline(color,opacity);
28922  const float nbrightness = brightness<0?0:(brightness>2?2:brightness);
28923  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
28924  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1);
28925  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2);
28926  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2);
28927  if (ny0<height() && ny2>=0) {
28928  if ((nx1 - nx0)*(ny2 - ny0) - (nx2 - nx0)*(ny1 - ny0)<0)
28929  _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xl,xr,y,color,opacity,nbrightness);
28930  else
28931  _cimg_for_triangle1(*this,xl,xr,y,nx0,ny0,nx1,ny1,nx2,ny2) _draw_scanline(xr,xl,y,color,opacity,nbrightness);
28932  }
28933  return *this;
28934  }
28935 
28937 
28947  template<typename tc>
28948  CImg<T>& draw_triangle(const int x0, const int y0,
28949  const int x1, const int y1,
28950  const int x2, const int y2,
28951  const tc *const color, const float opacity=1) {
28952  if (is_empty()) return *this;
28953  if (!color)
28954  throw CImgArgumentException(_cimg_instance
28955  "draw_triangle(): Specified color is (null).",
28956  cimg_instance);
28957  _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
28958  return *this;
28959  }
28960 
28962 
28973  template<typename tc>
28974  CImg<T>& draw_triangle(const int x0, const int y0,
28975  const int x1, const int y1,
28976  const int x2, const int y2,
28977  const tc *const color, const float opacity,
28978  const unsigned int pattern) {
28979  if (is_empty()) return *this;
28980  if (!color)
28981  throw CImgArgumentException(_cimg_instance
28982  "draw_triangle(): Specified color is (null).",
28983  cimg_instance);
28984  draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
28985  draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
28986  draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
28987  return *this;
28988  }
28989 
28991 
29006  template<typename tz, typename tc>
29008  const int x0, const int y0, const float z0,
29009  const int x1, const int y1, const float z1,
29010  const int x2, const int y2, const float z2,
29011  const tc *const color, const float opacity=1,
29012  const float brightness=1) {
29013  typedef typename cimg::superset<tz,float>::type tzfloat;
29014  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29015  if (!color)
29016  throw CImgArgumentException(_cimg_instance
29017  "draw_triangle(): Specified color is (null).",
29018  cimg_instance);
29019  if (!is_sameXY(zbuffer))
29020  throw CImgArgumentException(_cimg_instance
29021  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
29022  cimg_instance,
29023  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
29024  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29025  const float
29026  nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
29027  nbrightness = brightness<0?0:(brightness>2?2:brightness);
29028  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd;
29029  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
29030  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
29031  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1);
29032  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2);
29033  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2);
29034  if (ny0>=height() || ny2<0) return *this;
29035  tzfloat
29036  pzl = (nz1 - nz0)/(ny1 - ny0),
29037  pzr = (nz2 - nz0)/(ny2 - ny0),
29038  pzn = (nz2 - nz1)/(ny2 - ny1),
29039  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29040  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
29041  _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
29042  if (y==ny1) { zl = nz1; pzl = pzn; }
29043  int xleft = xleft0, xright = xright0;
29044  tzfloat zleft = zl, zright = zr;
29045  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright);
29046  const int dx = xright - xleft;
29047  const tzfloat pentez = (zright - zleft)/dx;
29048  if (xleft<0 && dx) zleft-=xleft*(zright - zleft)/dx;
29049  if (xleft<0) xleft = 0;
29050  if (xright>=width()-1) xright = width() - 1;
29051  T* ptrd = data(xleft,y,0,0);
29052  tz *ptrz = zbuffer.data(xleft,y);
29053  if (opacity>=1) {
29054  if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29055  if (zleft>=(tzfloat)*ptrz) {
29056  *ptrz = (tz)zleft;
29057  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
29058  ptrd-=offx;
29059  }
29060  zleft+=pentez;
29061  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29062  if (zleft>=(tzfloat)*ptrz) {
29063  *ptrz = (tz)zleft;
29064  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nbrightness*(*col++)); ptrd+=whd; }
29065  ptrd-=offx;
29066  }
29067  zleft+=pentez;
29068  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29069  if (zleft>=(tzfloat)*ptrz) {
29070  *ptrz = (tz)zleft;
29071  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval); ptrd+=whd; }
29072  ptrd-=offx;
29073  }
29074  zleft+=pentez;
29075  }
29076  } else {
29077  if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29078  if (zleft>=(tzfloat)*ptrz) {
29079  *ptrz = (tz)zleft;
29080  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity**(col++) + *ptrd*copacity); ptrd+=whd; }
29081  ptrd-=offx;
29082  }
29083  zleft+=pentez;
29084  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29085  if (zleft>=(tzfloat)*ptrz) {
29086  *ptrz = (tz)zleft;
29087  const tc *col = color; cimg_forC(*this,c) { *ptrd = (T)(nopacity*nbrightness**(col++) + *ptrd*copacity); ptrd+=whd; }
29088  ptrd-=offx;
29089  }
29090  zleft+=pentez;
29091  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29092  if (zleft>=(tzfloat)*ptrz) {
29093  *ptrz = (tz)zleft;
29094  const tc *col = color;
29095  cimg_forC(*this,c) {
29096  const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
29097  *ptrd = (T)(nopacity*val + *ptrd*copacity);
29098  ptrd+=whd;
29099  }
29100  ptrd-=offx;
29101  }
29102  zleft+=pentez;
29103  }
29104  }
29105  zr+=pzr; zl+=pzl;
29106  }
29107  return *this;
29108  }
29109 
29111 
29124  template<typename tc>
29125  CImg<T>& draw_triangle(const int x0, const int y0,
29126  const int x1, const int y1,
29127  const int x2, const int y2,
29128  const tc *const color,
29129  const float brightness0,
29130  const float brightness1,
29131  const float brightness2,
29132  const float opacity=1) {
29133  if (is_empty()) return *this;
29134  if (!color)
29135  throw CImgArgumentException(_cimg_instance
29136  "draw_triangle(): Specified color is (null).",
29137  cimg_instance);
29138  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29139  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29140  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd-1;
29141  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29142  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
29143  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
29144  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
29145  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nc0,nc1);
29146  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nc0,nc2);
29147  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nc1,nc2);
29148  if (ny0>=height() || ny2<0) return *this;
29149  _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
29150  int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
29151  if (xright<xleft) cimg::swap(xleft,xright,cleft,cright);
29152  const int
29153  dx = xright - xleft,
29154  dc = cright>cleft?cright - cleft:cleft - cright,
29155  rc = dx?(cright - cleft)/dx:0,
29156  sc = cright>cleft?1:-1,
29157  ndc = dc-(dx?dx*(dc/dx):0);
29158  int errc = dx>>1;
29159  if (xleft<0 && dx) cleft-=xleft*(cright - cleft)/dx;
29160  if (xleft<0) xleft = 0;
29161  if (xright>=width()-1) xright = width() - 1;
29162  T* ptrd = data(xleft,y);
29163  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
29164  const tc *col = color;
29165  cimg_forC(*this,c) {
29166  *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
29167  ptrd+=whd;
29168  }
29169  ptrd-=offx;
29170  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
29171  } else for (int x = xleft; x<=xright; ++x) {
29172  const tc *col = color;
29173  cimg_forC(*this,c) {
29174  const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
29175  *ptrd = (T)(nopacity*val + *ptrd*copacity);
29176  ptrd+=whd;
29177  }
29178  ptrd-=offx;
29179  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
29180  }
29181  }
29182  return *this;
29183  }
29184 
29186  template<typename tz, typename tc>
29188  const int x0, const int y0, const float z0,
29189  const int x1, const int y1, const float z1,
29190  const int x2, const int y2, const float z2,
29191  const tc *const color,
29192  const float brightness0,
29193  const float brightness1,
29194  const float brightness2,
29195  const float opacity=1) {
29196  typedef typename cimg::superset<tz,float>::type tzfloat;
29197  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29198  if (!color)
29199  throw CImgArgumentException(_cimg_instance
29200  "draw_triangle(): Specified color is (null).",
29201  cimg_instance);
29202  if (!is_sameXY(zbuffer))
29203  throw CImgArgumentException(_cimg_instance
29204  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
29205  cimg_instance,
29206  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
29207  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29208  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29209  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd;
29210  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29211  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
29212  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
29213  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
29214  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
29215  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nz0,nz1,nc0,nc1);
29216  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nz0,nz2,nc0,nc2);
29217  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nz1,nz2,nc1,nc2);
29218  if (ny0>=height() || ny2<0) return *this;
29219  tzfloat
29220  pzl = (nz1 - nz0)/(ny1 - ny0),
29221  pzr = (nz2 - nz0)/(ny2 - ny0),
29222  pzn = (nz2 - nz1)/(ny2 - ny1),
29223  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29224  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
29225  _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
29226  if (y==ny1) { zl = nz1; pzl = pzn; }
29227  int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
29228  tzfloat zleft = zl, zright = zr;
29229  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,cleft,cright);
29230  const int
29231  dx = xright - xleft,
29232  dc = cright>cleft?cright - cleft:cleft - cright,
29233  rc = dx?(cright-cleft)/dx:0,
29234  sc = cright>cleft?1:-1,
29235  ndc = dc-(dx?dx*(dc/dx):0);
29236  const tzfloat pentez = (zright - zleft)/dx;
29237  int errc = dx>>1;
29238  if (xleft<0 && dx) {
29239  cleft-=xleft*(cright - cleft)/dx;
29240  zleft-=xleft*(zright - zleft)/dx;
29241  }
29242  if (xleft<0) xleft = 0;
29243  if (xright>=width()-1) xright = width()-1;
29244  T *ptrd = data(xleft,y);
29245  tz *ptrz = zbuffer.data(xleft,y);
29246  if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
29247  if (zleft>=(tzfloat)*ptrz) {
29248  *ptrz = (tz)zleft;
29249  const tc *col = color;
29250  cimg_forC(*this,c) {
29251  *ptrd = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
29252  ptrd+=whd;
29253  }
29254  ptrd-=offx;
29255  }
29256  zleft+=pentez;
29257  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
29258  } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
29259  if (zleft>=(tzfloat)*ptrz) {
29260  *ptrz = (tz)zleft;
29261  const tc *col = color;
29262  cimg_forC(*this,c) {
29263  const T val = (T)(cleft<256?cleft**(col++)/256:((512-cleft)**(col++)+(cleft-256)*maxval)/256);
29264  *ptrd = (T)(nopacity*val + *ptrd*copacity);
29265  ptrd+=whd;
29266  }
29267  ptrd-=offx;
29268  }
29269  zleft+=pentez;
29270  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
29271  }
29272  zr+=pzr; zl+=pzl;
29273  }
29274  return *this;
29275  }
29276 
29278 
29290  template<typename tc1, typename tc2, typename tc3>
29291  CImg<T>& draw_triangle(const int x0, const int y0,
29292  const int x1, const int y1,
29293  const int x2, const int y2,
29294  const tc1 *const color1,
29295  const tc2 *const color2,
29296  const tc3 *const color3,
29297  const float opacity=1) {
29298  const unsigned char one = 1;
29299  cimg_forC(*this,c) get_shared_channel(c).draw_triangle(x0,y0,x1,y1,x2,y2,&one,color1[c],color2[c],color3[c],opacity);
29300  return *this;
29301  }
29302 
29304 
29321  template<typename tc>
29322  CImg<T>& draw_triangle(const int x0, const int y0,
29323  const int x1, const int y1,
29324  const int x2, const int y2,
29325  const CImg<tc>& texture,
29326  const int tx0, const int ty0,
29327  const int tx1, const int ty1,
29328  const int tx2, const int ty2,
29329  const float opacity=1,
29330  const float brightness=1) {
29331  if (is_empty()) return *this;
29332  if (texture._depth>1 || texture._spectrum<_spectrum)
29333  throw CImgArgumentException(_cimg_instance
29334  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
29335  cimg_instance,
29336  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29337  if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
29338  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29339  const float
29340  nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
29341  nbrightness = brightness<0?0:(brightness>2?2:brightness);
29342  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
29343  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29344  ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2;
29345  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1);
29346  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2);
29347  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2);
29348  if (ny0>=height() || ny2<0) return *this;
29349  _cimg_for_triangle3(*this,xleft0,txleft0,tyleft0,xright0,txright0,tyright0,y,
29350  nx0,ny0,ntx0,nty0,nx1,ny1,ntx1,nty1,nx2,ny2,ntx2,nty2) {
29351  int
29352  xleft = xleft0, xright = xright0,
29353  txleft = txleft0, txright = txright0,
29354  tyleft = tyleft0, tyright = tyright0;
29355  if (xright<xleft) cimg::swap(xleft,xright,txleft,txright,tyleft,tyright);
29356  const int
29357  dx = xright - xleft,
29358  dtx = txright>txleft?txright - txleft:txleft - txright,
29359  dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
29360  rtx = dx?(txright - txleft)/dx:0,
29361  rty = dx?(tyright - tyleft)/dx:0,
29362  stx = txright>txleft?1:-1,
29363  sty = tyright>tyleft?1:-1,
29364  ndtx = dtx - (dx?dx*(dtx/dx):0),
29365  ndty = dty - (dx?dx*(dty/dx):0);
29366  int errtx = dx>>1, errty = errtx;
29367  if (xleft<0 && dx) {
29368  txleft-=xleft*(txright - txleft)/dx;
29369  tyleft-=xleft*(tyright - tyleft)/dx;
29370  }
29371  if (xleft<0) xleft = 0;
29372  if (xright>=width()-1) xright = width()-1;
29373  T* ptrd = data(xleft,y,0,0);
29374  if (opacity>=1) {
29375  if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
29376  const tc *col = texture.data(txleft,tyleft);
29377  cimg_forC(*this,c) {
29378  *ptrd = (T)*col;
29379  ptrd+=whd; col+=twhd;
29380  }
29381  ptrd-=offx;
29382  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29383  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29384  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
29385  const tc *col = texture.data(txleft,tyleft);
29386  cimg_forC(*this,c) {
29387  *ptrd = (T)(nbrightness**col);
29388  ptrd+=whd; col+=twhd;
29389  }
29390  ptrd-=offx;
29391  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29392  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29393  } else for (int x = xleft; x<=xright; ++x) {
29394  const tc *col = texture.data(txleft,tyleft);
29395  cimg_forC(*this,c) {
29396  *ptrd = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
29397  ptrd+=whd; col+=twhd;
29398  }
29399  ptrd-=offx;
29400  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29401  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29402  }
29403  } else {
29404  if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
29405  const tc *col = texture.data(txleft,tyleft);
29406  cimg_forC(*this,c) {
29407  *ptrd = (T)(nopacity**col + *ptrd*copacity);
29408  ptrd+=whd; col+=twhd;
29409  }
29410  ptrd-=offx;
29411  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29412  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29413  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
29414  const tc *col = texture.data(txleft,tyleft);
29415  cimg_forC(*this,c) {
29416  *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
29417  ptrd+=whd; col+=twhd;
29418  }
29419  ptrd-=offx;
29420  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29421  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29422  } else for (int x = xleft; x<=xright; ++x) {
29423  const tc *col = texture.data(txleft,tyleft);
29424  cimg_forC(*this,c) {
29425  const T val = (T)((2-nbrightness)**(col++) + (nbrightness-1)*maxval);
29426  *ptrd = (T)(nopacity*val + *ptrd*copacity);
29427  ptrd+=whd; col+=twhd;
29428  }
29429  ptrd-=offx;
29430  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
29431  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
29432  }
29433  }
29434  }
29435  return *this;
29436  }
29437 
29439  template<typename tc>
29440  CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
29441  const int x1, const int y1, const float z1,
29442  const int x2, const int y2, const float z2,
29443  const CImg<tc>& texture,
29444  const int tx0, const int ty0,
29445  const int tx1, const int ty1,
29446  const int tx2, const int ty2,
29447  const float opacity=1,
29448  const float brightness=1) {
29449  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29450  if (texture._depth>1 || texture._spectrum<_spectrum)
29451  throw CImgArgumentException(_cimg_instance
29452  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
29453  cimg_instance,
29454  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29455  if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
29456  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29457  const float
29458  nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
29459  nbrightness = brightness<0?0:(brightness>2?2:brightness);
29460  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
29461  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
29462  float
29463  ntx0 = tx0/z0, nty0 = ty0/z0,
29464  ntx1 = tx1/z1, nty1 = ty1/z1,
29465  ntx2 = tx2/z2, nty2 = ty2/z2,
29466  nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
29467  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
29468  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
29469  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
29470  if (ny0>=height() || ny2<0) return *this;
29471  float
29472  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
29473  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
29474  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
29475  ptyl = (nty1 - nty0)/(ny1 - ny0),
29476  ptyr = (nty2 - nty0)/(ny2 - ny0),
29477  ptyn = (nty2 - nty1)/(ny2 - ny1),
29478  pzl = (nz1 - nz0)/(ny1 - ny0),
29479  pzr = (nz2 - nz0)/(ny2 - ny0),
29480  pzn = (nz2 - nz1)/(ny2 - ny1),
29481  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29482  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
29483  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
29484  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
29485  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
29486  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
29487  _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
29488  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
29489  int xleft = xleft0, xright = xright0;
29490  float
29491  zleft = zl, zright = zr,
29492  txleft = txl, txright = txr,
29493  tyleft = tyl, tyright = tyr;
29494  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
29495  const int dx = xright - xleft;
29496  const float
29497  pentez = (zright - zleft)/dx,
29498  pentetx = (txright - txleft)/dx,
29499  pentety = (tyright - tyleft)/dx;
29500  if (xleft<0 && dx) {
29501  zleft-=xleft*(zright - zleft)/dx;
29502  txleft-=xleft*(txright - txleft)/dx;
29503  tyleft-=xleft*(tyright - tyleft)/dx;
29504  }
29505  if (xleft<0) xleft = 0;
29506  if (xright>=width()-1) xright = width()-1;
29507  T* ptrd = data(xleft,y,0,0);
29508  if (opacity>=1) {
29509  if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
29510  const float invz = 1/zleft;
29511  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29512  cimg_forC(*this,c) {
29513  *ptrd = (T)*col;
29514  ptrd+=whd; col+=twhd;
29515  }
29516  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29517  } else if (nbrightness<1) for (int x=xleft; x<=xright; ++x) {
29518  const float invz = 1/zleft;
29519  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29520  cimg_forC(*this,c) {
29521  *ptrd = (T)(nbrightness**col);
29522  ptrd+=whd; col+=twhd;
29523  }
29524  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29525  } else for (int x = xleft; x<=xright; ++x) {
29526  const float invz = 1/zleft;
29527  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29528  cimg_forC(*this,c) {
29529  *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
29530  ptrd+=whd; col+=twhd;
29531  }
29532  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29533  }
29534  } else {
29535  if (nbrightness==1) for (int x = xleft; x<=xright; ++x) {
29536  const float invz = 1/zleft;
29537  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29538  cimg_forC(*this,c) {
29539  *ptrd = (T)(nopacity**col + *ptrd*copacity);
29540  ptrd+=whd; col+=twhd;
29541  }
29542  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29543  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x) {
29544  const float invz = 1/zleft;
29545  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29546  cimg_forC(*this,c) {
29547  *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
29548  ptrd+=whd; col+=twhd;
29549  }
29550  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29551  } else for (int x = xleft; x<=xright; ++x) {
29552  const float invz = 1/zleft;
29553  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29554  cimg_forC(*this,c) {
29555  const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
29556  *ptrd = (T)(nopacity*val + *ptrd*copacity);
29557  ptrd+=whd; col+=twhd;
29558  }
29559  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29560  }
29561  }
29562  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
29563  }
29564  return *this;
29565  }
29566 
29568  template<typename tz, typename tc>
29570  const int x0, const int y0, const float z0,
29571  const int x1, const int y1, const float z1,
29572  const int x2, const int y2, const float z2,
29573  const CImg<tc>& texture,
29574  const int tx0, const int ty0,
29575  const int tx1, const int ty1,
29576  const int tx2, const int ty2,
29577  const float opacity=1,
29578  const float brightness=1) {
29579  typedef typename cimg::superset<tz,float>::type tzfloat;
29580  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29581  if (!is_sameXY(zbuffer))
29582  throw CImgArgumentException(_cimg_instance
29583  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
29584  cimg_instance,
29585  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
29586 
29587  if (texture._depth>1 || texture._spectrum<_spectrum)
29588  throw CImgArgumentException(_cimg_instance
29589  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
29590  cimg_instance,
29591  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29592  if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
29593  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29594  const float
29595  nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0),
29596  nbrightness = brightness<0?0:(brightness>2?2:brightness);
29597  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd;
29598  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2;
29599  float
29600  ntx0 = tx0/z0, nty0 = ty0/z0,
29601  ntx1 = tx1/z1, nty1 = ty1/z1,
29602  ntx2 = tx2/z2, nty2 = ty2/z2;
29603  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
29604  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1);
29605  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2);
29606  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2);
29607  if (ny0>=height() || ny2<0) return *this;
29608  float
29609  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
29610  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
29611  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
29612  ptyl = (nty1 - nty0)/(ny1 - ny0),
29613  ptyr = (nty2 - nty0)/(ny2 - ny0),
29614  ptyn = (nty2 - nty1)/(ny2 - ny1),
29615  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
29616  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
29617  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
29618  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
29619  tzfloat
29620  pzl = (nz1 - nz0)/(ny1 - ny0),
29621  pzr = (nz2 - nz0)/(ny2 - ny0),
29622  pzn = (nz2 - nz1)/(ny2 - ny1),
29623  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29624  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
29625  _cimg_for_triangle1(*this,xleft0,xright0,y,nx0,ny0,nx1,ny1,nx2,ny2) {
29626  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
29627  int xleft = xleft0, xright = xright0;
29628  float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
29629  tzfloat zleft = zl, zright = zr;
29630  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright);
29631  const int dx = xright - xleft;
29632  const float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
29633  const tzfloat pentez = (zright - zleft)/dx;
29634  if (xleft<0 && dx) {
29635  zleft-=xleft*(zright - zleft)/dx;
29636  txleft-=xleft*(txright - txleft)/dx;
29637  tyleft-=xleft*(tyright - tyleft)/dx;
29638  }
29639  if (xleft<0) xleft = 0;
29640  if (xright>=width()-1) xright = width()-1;
29641  T *ptrd = data(xleft,y,0,0);
29642  tz *ptrz = zbuffer.data(xleft,y);
29643  if (opacity>=1) {
29644  if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29645  if (zleft>=(tzfloat)*ptrz) {
29646  *ptrz = (tz)zleft;
29647  const tzfloat invz = 1/zleft;
29648  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29649  cimg_forC(*this,c) {
29650  *ptrd = (T)*col;
29651  ptrd+=whd; col+=twhd;
29652  }
29653  ptrd-=offx;
29654  }
29655  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29656  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29657  if (zleft>=(tzfloat)*ptrz) {
29658  *ptrz = (tz)zleft;
29659  const tzfloat invz = 1/zleft;
29660  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29661  cimg_forC(*this,c) {
29662  *ptrd = (T)(nbrightness**col);
29663  ptrd+=whd; col+=twhd;
29664  }
29665  ptrd-=offx;
29666  }
29667  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29668  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29669  if (zleft>=(tzfloat)*ptrz) {
29670  *ptrz = (tz)zleft;
29671  const tzfloat invz = 1/zleft;
29672  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29673  cimg_forC(*this,c) {
29674  *ptrd = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
29675  ptrd+=whd; col+=twhd;
29676  }
29677  ptrd-=offx;
29678  }
29679  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29680  }
29681  } else {
29682  if (nbrightness==1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29683  if (zleft>=(tzfloat)*ptrz) {
29684  *ptrz = (tz)zleft;
29685  const tzfloat invz = 1/zleft;
29686  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29687  cimg_forC(*this,c) {
29688  *ptrd = (T)(nopacity**col + *ptrd*copacity);
29689  ptrd+=whd; col+=twhd;
29690  }
29691  ptrd-=offx;
29692  }
29693  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29694  } else if (nbrightness<1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29695  if (zleft>=(tzfloat)*ptrz) {
29696  *ptrz = (tz)zleft;
29697  const tzfloat invz = 1/zleft;
29698  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29699  cimg_forC(*this,c) {
29700  *ptrd = (T)(nopacity*nbrightness**col + *ptrd*copacity);
29701  ptrd+=whd; col+=twhd;
29702  }
29703  ptrd-=offx;
29704  }
29705  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29706  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29707  if (zleft>=(tzfloat)*ptrz) {
29708  *ptrz = (tz)zleft;
29709  const tzfloat invz = 1/zleft;
29710  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
29711  cimg_forC(*this,c) {
29712  const T val = (T)((2-nbrightness)**col + (nbrightness-1)*maxval);
29713  *ptrd = (T)(nopacity*val + *ptrd*copacity);
29714  ptrd+=whd; col+=twhd;
29715  }
29716  ptrd-=offx;
29717  }
29718  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
29719  }
29720  }
29721  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
29722  }
29723  return *this;
29724  }
29725 
29727 
29744  template<typename tc, typename tl>
29745  CImg<T>& draw_triangle(const int x0, const int y0,
29746  const int x1, const int y1,
29747  const int x2, const int y2,
29748  const tc *const color,
29749  const CImg<tl>& light,
29750  const int lx0, const int ly0,
29751  const int lx1, const int ly1,
29752  const int lx2, const int ly2,
29753  const float opacity=1) {
29754  if (is_empty()) return *this;
29755  if (!color)
29756  throw CImgArgumentException(_cimg_instance
29757  "draw_triangle(): Specified color is (null).",
29758  cimg_instance);
29759  if (light._depth>1 || light._spectrum<_spectrum)
29760  throw CImgArgumentException(_cimg_instance
29761  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
29762  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
29763  if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,color,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
29764  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29765  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29766  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29767  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
29768  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd-1;
29769  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1);
29770  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2);
29771  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2);
29772  if (ny0>=height() || ny2<0) return *this;
29773  _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
29774  nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
29775  int
29776  xleft = xleft0, xright = xright0,
29777  lxleft = lxleft0, lxright = lxright0,
29778  lyleft = lyleft0, lyright = lyright0;
29779  if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright);
29780  const int
29781  dx = xright - xleft,
29782  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
29783  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
29784  rlx = dx?(lxright - lxleft)/dx:0,
29785  rly = dx?(lyright - lyleft)/dx:0,
29786  slx = lxright>lxleft?1:-1,
29787  sly = lyright>lyleft?1:-1,
29788  ndlx = dlx - (dx?dx*(dlx/dx):0),
29789  ndly = dly - (dx?dx*(dly/dx):0);
29790  int errlx = dx>>1, errly = errlx;
29791  if (xleft<0 && dx) {
29792  lxleft-=xleft*(lxright - lxleft)/dx;
29793  lyleft-=xleft*(lyright - lyleft)/dx;
29794  }
29795  if (xleft<0) xleft = 0;
29796  if (xright>=width()-1) xright = width()-1;
29797  T* ptrd = data(xleft,y,0,0);
29798  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
29799  const tc *col = color;
29800  cimg_forC(*this,c) {
29801  const tl l = light(lxleft,lyleft,c);
29802  *ptrd = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
29803  ptrd+=whd;
29804  }
29805  ptrd-=offx;
29806  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29807  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29808  } else for (int x = xleft; x<=xright; ++x) {
29809  const tc *col = color;
29810  cimg_forC(*this,c) {
29811  const tl l = light(lxleft,lyleft,c);
29812  const T val = (T)(l<1?l**(col++):((2-l)**(col++)+(l-1)*maxval));
29813  *ptrd = (T)(nopacity*val + *ptrd*copacity);
29814  ptrd+=whd;
29815  }
29816  ptrd-=offx;
29817  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29818  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29819  }
29820  }
29821  return *this;
29822  }
29823 
29825  template<typename tz, typename tc, typename tl>
29827  const int x0, const int y0, const float z0,
29828  const int x1, const int y1, const float z1,
29829  const int x2, const int y2, const float z2,
29830  const tc *const color,
29831  const CImg<tl>& light,
29832  const int lx0, const int ly0,
29833  const int lx1, const int ly1,
29834  const int lx2, const int ly2,
29835  const float opacity=1) {
29836  typedef typename cimg::superset<tz,float>::type tzfloat;
29837  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
29838  if (!color)
29839  throw CImgArgumentException(_cimg_instance
29840  "draw_triangle(): Specified color is (null).",
29841  cimg_instance);
29842  if (light._depth>1 || light._spectrum<_spectrum)
29843  throw CImgArgumentException(_cimg_instance
29844  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
29845  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
29846  if (!is_sameXY(zbuffer))
29847  throw CImgArgumentException(_cimg_instance
29848  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
29849  cimg_instance,
29850  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
29851  if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
29852  +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
29853  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29854  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29855  const long whd = (long)_width*_height*_depth, offx = _spectrum*whd;
29856  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29857  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
29858  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
29859  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,nlx0,nlx1,nly0,nly1,nz0,nz1);
29860  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,nlx0,nlx2,nly0,nly2,nz0,nz2);
29861  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,nlx1,nlx2,nly1,nly2,nz1,nz2);
29862  if (ny0>=height() || ny2<0) return *this;
29863  tzfloat
29864  pzl = (nz1 - nz0)/(ny1 - ny0),
29865  pzr = (nz2 - nz0)/(ny2 - ny0),
29866  pzn = (nz2 - nz1)/(ny2 - ny1),
29867  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
29868  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
29869  _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
29870  nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
29871  if (y==ny1) { zl = nz1; pzl = pzn; }
29872  int
29873  xleft = xleft0, xright = xright0,
29874  lxleft = lxleft0, lxright = lxright0,
29875  lyleft = lyleft0, lyright = lyright0;
29876  tzfloat zleft = zl, zright = zr;
29877  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,lxleft,lxright,lyleft,lyright);
29878  const int
29879  dx = xright - xleft,
29880  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
29881  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
29882  rlx = dx?(lxright - lxleft)/dx:0,
29883  rly = dx?(lyright - lyleft)/dx:0,
29884  slx = lxright>lxleft?1:-1,
29885  sly = lyright>lyleft?1:-1,
29886  ndlx = dlx - (dx?dx*(dlx/dx):0),
29887  ndly = dly - (dx?dx*(dly/dx):0);
29888  const tzfloat pentez = (zright - zleft)/dx;
29889  int errlx = dx>>1, errly = errlx;
29890  if (xleft<0 && dx) {
29891  zleft-=xleft*(zright - zleft)/dx;
29892  lxleft-=xleft*(lxright - lxleft)/dx;
29893  lyleft-=xleft*(lyright - lyleft)/dx;
29894  }
29895  if (xleft<0) xleft = 0;
29896  if (xright>=width()-1) xright = width()-1;
29897  T *ptrd = data(xleft,y,0,0);
29898  tz *ptrz = zbuffer.data(xleft,y);
29899  if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29900  if (zleft>=(tzfloat)*ptrz) {
29901  *ptrz = (tz)zleft;
29902  const tc *col = color;
29903  cimg_forC(*this,c) {
29904  const tl l = light(lxleft,lyleft,c);
29905  const tc cval = *(col++);
29906  *ptrd = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
29907  ptrd+=whd;
29908  }
29909  ptrd-=offx;
29910  }
29911  zleft+=pentez;
29912  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29913  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29914  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
29915  if (zleft>=(tzfloat)*ptrz) {
29916  *ptrz = (tz)zleft;
29917  const tc *col = color;
29918  cimg_forC(*this,c) {
29919  const tl l = light(lxleft,lyleft,c);
29920  const tc cval = *(col++);
29921  const T val = (T)(l<1?l*cval:(2-l)*cval+(l-1)*maxval);
29922  *ptrd = (T)(nopacity*val + *ptrd*copacity);
29923  ptrd+=whd;
29924  }
29925  ptrd-=offx;
29926  }
29927  zleft+=pentez;
29928  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
29929  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
29930  }
29931  zr+=pzr; zl+=pzl;
29932  }
29933  return *this;
29934  }
29935 
29937 
29956  template<typename tc>
29957  CImg<T>& draw_triangle(const int x0, const int y0,
29958  const int x1, const int y1,
29959  const int x2, const int y2,
29960  const CImg<tc>& texture,
29961  const int tx0, const int ty0,
29962  const int tx1, const int ty1,
29963  const int tx2, const int ty2,
29964  const float brightness0,
29965  const float brightness1,
29966  const float brightness2,
29967  const float opacity=1) {
29968  if (is_empty()) return *this;
29969  if (texture._depth>1 || texture._spectrum<_spectrum)
29970  throw CImgArgumentException(_cimg_instance
29971  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
29972  cimg_instance,
29973  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
29974  if (is_overlapped(texture))
29975  return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,brightness0,brightness1,brightness2,opacity);
29976  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
29977  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
29978  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
29979  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
29980  ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
29981  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
29982  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
29983  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
29984  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nc0,nc1);
29985  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nc0,nc2);
29986  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nc1,nc2);
29987  if (ny0>=height() || ny2<0) return *this;
29988  _cimg_for_triangle4(*this,xleft0,cleft0,txleft0,tyleft0,xright0,cright0,txright0,tyright0,y,
29989  nx0,ny0,nc0,ntx0,nty0,nx1,ny1,nc1,ntx1,nty1,nx2,ny2,nc2,ntx2,nty2) {
29990  int
29991  xleft = xleft0, xright = xright0,
29992  cleft = cleft0, cright = cright0,
29993  txleft = txleft0, txright = txright0,
29994  tyleft = tyleft0, tyright = tyright0;
29995  if (xright<xleft) cimg::swap(xleft,xright,cleft,cright,txleft,txright,tyleft,tyright);
29996  const int
29997  dx = xright - xleft,
29998  dc = cright>cleft?cright - cleft:cleft - cright,
29999  dtx = txright>txleft?txright - txleft:txleft - txright,
30000  dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
30001  rc = dx?(cright - cleft)/dx:0,
30002  rtx = dx?(txright - txleft)/dx:0,
30003  rty = dx?(tyright - tyleft)/dx:0,
30004  sc = cright>cleft?1:-1,
30005  stx = txright>txleft?1:-1,
30006  sty = tyright>tyleft?1:-1,
30007  ndc = dc - (dx?dx*(dc/dx):0),
30008  ndtx = dtx - (dx?dx*(dtx/dx):0),
30009  ndty = dty - (dx?dx*(dty/dx):0);
30010  int errc = dx>>1, errtx = errc, errty = errc;
30011  if (xleft<0 && dx) {
30012  cleft-=xleft*(cright - cleft)/dx;
30013  txleft-=xleft*(txright - txleft)/dx;
30014  tyleft-=xleft*(tyright - tyleft)/dx;
30015  }
30016  if (xleft<0) xleft = 0;
30017  if (xright>=width()-1) xright = width()-1;
30018  T* ptrd = data(xleft,y,0,0);
30019  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
30020  const tc *col = texture.data(txleft,tyleft);
30021  cimg_forC(*this,c) {
30022  *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30023  ptrd+=whd; col+=twhd;
30024  }
30025  ptrd-=offx;
30026  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30027  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
30028  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
30029  } else for (int x = xleft; x<=xright; ++x) {
30030  const tc *col = texture.data(txleft,tyleft);
30031  cimg_forC(*this,c) {
30032  const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30033  *ptrd = (T)(nopacity*val + *ptrd*copacity);
30034  ptrd+=whd; col+=twhd;
30035  }
30036  ptrd-=offx;
30037  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30038  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
30039  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
30040  }
30041  }
30042  return *this;
30043  }
30044 
30046  template<typename tc>
30047  CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
30048  const int x1, const int y1, const float z1,
30049  const int x2, const int y2, const float z2,
30050  const CImg<tc>& texture,
30051  const int tx0, const int ty0,
30052  const int tx1, const int ty1,
30053  const int tx2, const int ty2,
30054  const float brightness0,
30055  const float brightness1,
30056  const float brightness2,
30057  const float opacity=1) {
30058  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
30059  if (texture._depth>1 || texture._spectrum<_spectrum)
30060  throw CImgArgumentException(_cimg_instance
30061  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
30062  cimg_instance,
30063  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30064  if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
30065  brightness0,brightness1,brightness2,opacity);
30066  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30067  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30068  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
30069  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30070  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
30071  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
30072  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
30073  float
30074  ntx0 = tx0/z0, nty0 = ty0/z0,
30075  ntx1 = tx1/z1, nty1 = ty1/z1,
30076  ntx2 = tx2/z2, nty2 = ty2/z2,
30077  nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
30078  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
30079  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
30080  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
30081  if (ny0>=height() || ny2<0) return *this;
30082  float
30083  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
30084  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
30085  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
30086  ptyl = (nty1 - nty0)/(ny1 - ny0),
30087  ptyr = (nty2 - nty0)/(ny2 - ny0),
30088  ptyn = (nty2 - nty1)/(ny2 - ny1),
30089  pzl = (nz1 - nz0)/(ny1 - ny0),
30090  pzr = (nz2 - nz0)/(ny2 - ny0),
30091  pzn = (nz2 - nz1)/(ny2 - ny1),
30092  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
30093  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
30094  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
30095  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
30096  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
30097  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
30098  _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
30099  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
30100  int
30101  xleft = xleft0, xright = xright0,
30102  cleft = cleft0, cright = cright0;
30103  float
30104  zleft = zl, zright = zr,
30105  txleft = txl, txright = txr,
30106  tyleft = tyl, tyright = tyr;
30107  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
30108  const int
30109  dx = xright - xleft,
30110  dc = cright>cleft?cright - cleft:cleft - cright,
30111  rc = dx?(cright - cleft)/dx:0,
30112  sc = cright>cleft?1:-1,
30113  ndc = dc - (dx?dx*(dc/dx):0);
30114  const float
30115  pentez = (zright - zleft)/dx,
30116  pentetx = (txright - txleft)/dx,
30117  pentety = (tyright - tyleft)/dx;
30118  int errc = dx>>1;
30119  if (xleft<0 && dx) {
30120  cleft-=xleft*(cright - cleft)/dx;
30121  zleft-=xleft*(zright - zleft)/dx;
30122  txleft-=xleft*(txright - txleft)/dx;
30123  tyleft-=xleft*(tyright - tyleft)/dx;
30124  }
30125  if (xleft<0) xleft = 0;
30126  if (xright>=width()-1) xright = width()-1;
30127  T* ptrd = data(xleft,y,0,0);
30128  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
30129  const float invz = 1/zleft;
30130  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30131  cimg_forC(*this,c) {
30132  *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30133  ptrd+=whd; col+=twhd;
30134  }
30135  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30136  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30137  } else for (int x = xleft; x<=xright; ++x) {
30138  const float invz = 1/zleft;
30139  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30140  cimg_forC(*this,c) {
30141  const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30142  *ptrd = (T)(nopacity*val + *ptrd*copacity);
30143  ptrd+=whd; col+=twhd;
30144  }
30145  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30146  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30147  }
30148  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
30149  }
30150  return *this;
30151  }
30152 
30154  template<typename tz, typename tc>
30156  const int x0, const int y0, const float z0,
30157  const int x1, const int y1, const float z1,
30158  const int x2, const int y2, const float z2,
30159  const CImg<tc>& texture,
30160  const int tx0, const int ty0,
30161  const int tx1, const int ty1,
30162  const int tx2, const int ty2,
30163  const float brightness0,
30164  const float brightness1,
30165  const float brightness2,
30166  const float opacity=1) {
30167  typedef typename cimg::superset<tz,float>::type tzfloat;
30168  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
30169  if (!is_sameXY(zbuffer))
30170  throw CImgArgumentException(_cimg_instance
30171  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
30172  cimg_instance,
30173  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
30174  if (texture._depth>1 || texture._spectrum<_spectrum)
30175  throw CImgArgumentException(_cimg_instance
30176  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
30177  cimg_instance,
30178  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30179  if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
30180  brightness0,brightness1,brightness2,opacity);
30181  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30182  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30183  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd;
30184  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30185  nc0 = (int)((brightness0<0.0f?0.0f:(brightness0>2.0f?2.0f:brightness0))*256.0f),
30186  nc1 = (int)((brightness1<0.0f?0.0f:(brightness1>2.0f?2.0f:brightness1))*256.0f),
30187  nc2 = (int)((brightness2<0.0f?0.0f:(brightness2>2.0f?2.0f:brightness2))*256.0f);
30188  float
30189  ntx0 = tx0/z0, nty0 = ty0/z0,
30190  ntx1 = tx1/z1, nty1 = ty1/z1,
30191  ntx2 = tx2/z2, nty2 = ty2/z2;
30192  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
30193  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nz0,nz1,nc0,nc1);
30194  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nz0,nz2,nc0,nc2);
30195  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nz1,nz2,nc1,nc2);
30196  if (ny0>=height() || ny2<0) return *this;
30197  float
30198  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
30199  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
30200  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
30201  ptyl = (nty1 - nty0)/(ny1 - ny0),
30202  ptyr = (nty2 - nty0)/(ny2 - ny0),
30203  ptyn = (nty2 - nty1)/(ny2 - ny1),
30204  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
30205  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
30206  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
30207  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
30208  tzfloat
30209  pzl = (nz1 - nz0)/(ny1 - ny0),
30210  pzr = (nz2 - nz0)/(ny2 - ny0),
30211  pzn = (nz2 - nz1)/(ny2 - ny1),
30212  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
30213  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
30214  _cimg_for_triangle2(*this,xleft0,cleft0,xright0,cright0,y,nx0,ny0,nc0,nx1,ny1,nc1,nx2,ny2,nc2) {
30215  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
30216  int xleft = xleft0, xright = xright0, cleft = cleft0, cright = cright0;
30217  float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
30218  tzfloat zleft = zl, zright = zr;
30219  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,cleft,cright);
30220  const int
30221  dx = xright - xleft,
30222  dc = cright>cleft?cright - cleft:cleft - cright,
30223  rc = dx?(cright - cleft)/dx:0,
30224  sc = cright>cleft?1:-1,
30225  ndc = dc - (dx?dx*(dc/dx):0);
30226  float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
30227  const tzfloat pentez = (zright - zleft)/dx;
30228  int errc = dx>>1;
30229  if (xleft<0 && dx) {
30230  cleft-=xleft*(cright - cleft)/dx;
30231  zleft-=xleft*(zright - zleft)/dx;
30232  txleft-=xleft*(txright - txleft)/dx;
30233  tyleft-=xleft*(tyright - tyleft)/dx;
30234  }
30235  if (xleft<0) xleft = 0;
30236  if (xright>=width()-1) xright = width()-1;
30237  T* ptrd = data(xleft,y);
30238  tz *ptrz = zbuffer.data(xleft,y);
30239  if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
30240  if (zleft>=(tzfloat)*ptrz) {
30241  *ptrz = (tz)zleft;
30242  const tzfloat invz = 1/zleft;
30243  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30244  cimg_forC(*this,c) {
30245  *ptrd = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30246  ptrd+=whd; col+=twhd;
30247  }
30248  ptrd-=offx;
30249  }
30250  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30251  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30252  } else for (int x = xleft; x<=xright; ++x, ++ptrd, ++ptrz) {
30253  if (zleft>=(tzfloat)*ptrz) {
30254  *ptrz = (tz)zleft;
30255  const tzfloat invz = 1/zleft;
30256  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30257  cimg_forC(*this,c) {
30258  const T val = (T)(cleft<256?cleft**col/256:((512-cleft)**col+(cleft-256)*maxval)/256);
30259  *ptrd = (T)(nopacity*val + *ptrd*copacity);
30260  ptrd+=whd; col+=twhd;
30261  }
30262  ptrd-=offx;
30263  }
30264  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30265  cleft+=rc+((errc-=ndc)<0?errc+=dx,sc:0);
30266  }
30267  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
30268  }
30269  return *this;
30270  }
30271 
30273 
30296  template<typename tc, typename tl>
30297  CImg<T>& draw_triangle(const int x0, const int y0,
30298  const int x1, const int y1,
30299  const int x2, const int y2,
30300  const CImg<tc>& texture,
30301  const int tx0, const int ty0,
30302  const int tx1, const int ty1,
30303  const int tx2, const int ty2,
30304  const CImg<tl>& light,
30305  const int lx0, const int ly0,
30306  const int lx1, const int ly1,
30307  const int lx2, const int ly2,
30308  const float opacity=1) {
30309  if (is_empty()) return *this;
30310  if (texture._depth>1 || texture._spectrum<_spectrum)
30311  throw CImgArgumentException(_cimg_instance
30312  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
30313  cimg_instance,
30314  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30315  if (light._depth>1 || light._spectrum<_spectrum)
30316  throw CImgArgumentException(_cimg_instance
30317  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
30318  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
30319  if (is_overlapped(texture)) return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30320  if (is_overlapped(light)) return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30321  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30322  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30323  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
30324  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30325  ntx0 = tx0, nty0 = ty0, ntx1 = tx1, nty1 = ty1, ntx2 = tx2, nty2 = ty2,
30326  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
30327  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1);
30328  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2);
30329  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2);
30330  if (ny0>=height() || ny2<0) return *this;
30331  _cimg_for_triangle5(*this,xleft0,lxleft0,lyleft0,txleft0,tyleft0,xright0,lxright0,lyright0,txright0,tyright0,y,
30332  nx0,ny0,nlx0,nly0,ntx0,nty0,nx1,ny1,nlx1,nly1,ntx1,nty1,nx2,ny2,nlx2,nly2,ntx2,nty2) {
30333  int
30334  xleft = xleft0, xright = xright0,
30335  lxleft = lxleft0, lxright = lxright0,
30336  lyleft = lyleft0, lyright = lyright0,
30337  txleft = txleft0, txright = txright0,
30338  tyleft = tyleft0, tyright = tyright0;
30339  if (xright<xleft) cimg::swap(xleft,xright,lxleft,lxright,lyleft,lyright,txleft,txright,tyleft,tyright);
30340  const int
30341  dx = xright - xleft,
30342  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
30343  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
30344  dtx = txright>txleft?txright - txleft:txleft - txright,
30345  dty = tyright>tyleft?tyright - tyleft:tyleft - tyright,
30346  rlx = dx?(lxright - lxleft)/dx:0,
30347  rly = dx?(lyright - lyleft)/dx:0,
30348  rtx = dx?(txright - txleft)/dx:0,
30349  rty = dx?(tyright - tyleft)/dx:0,
30350  slx = lxright>lxleft?1:-1,
30351  sly = lyright>lyleft?1:-1,
30352  stx = txright>txleft?1:-1,
30353  sty = tyright>tyleft?1:-1,
30354  ndlx = dlx - (dx?dx*(dlx/dx):0),
30355  ndly = dly - (dx?dx*(dly/dx):0),
30356  ndtx = dtx - (dx?dx*(dtx/dx):0),
30357  ndty = dty - (dx?dx*(dty/dx):0);
30358  int errlx = dx>>1, errly = errlx, errtx = errlx, errty = errlx;
30359  if (xleft<0 && dx) {
30360  lxleft-=xleft*(lxright - lxleft)/dx;
30361  lyleft-=xleft*(lyright - lyleft)/dx;
30362  txleft-=xleft*(txright - txleft)/dx;
30363  tyleft-=xleft*(tyright - tyleft)/dx;
30364  }
30365  if (xleft<0) xleft = 0;
30366  if (xright>=width()-1) xright = width()-1;
30367  T* ptrd = data(xleft,y,0,0);
30368  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
30369  const tc *col = texture.data(txleft,tyleft);
30370  cimg_forC(*this,c) {
30371  const tl l = light(lxleft,lyleft,c);
30372  *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30373  ptrd+=whd; col+=twhd;
30374  }
30375  ptrd-=offx;
30376  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30377  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30378  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
30379  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
30380  } else for (int x = xleft; x<=xright; ++x) {
30381  const tc *col = texture.data(txleft,tyleft);
30382  cimg_forC(*this,c) {
30383  const tl l = light(lxleft,lyleft,c);
30384  const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30385  *ptrd = (T)(nopacity*val + *ptrd*copacity);
30386  ptrd+=whd; col+=twhd;
30387  }
30388  ptrd-=offx;
30389  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30390  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30391  txleft+=rtx+((errtx-=ndtx)<0?errtx+=dx,stx:0);
30392  tyleft+=rty+((errty-=ndty)<0?errty+=dx,sty:0);
30393  }
30394  }
30395  return *this;
30396  }
30397 
30399  template<typename tc, typename tl>
30400  CImg<T>& draw_triangle(const int x0, const int y0, const float z0,
30401  const int x1, const int y1, const float z1,
30402  const int x2, const int y2, const float z2,
30403  const CImg<tc>& texture,
30404  const int tx0, const int ty0,
30405  const int tx1, const int ty1,
30406  const int tx2, const int ty2,
30407  const CImg<tl>& light,
30408  const int lx0, const int ly0,
30409  const int lx1, const int ly1,
30410  const int lx2, const int ly2,
30411  const float opacity=1) {
30412  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
30413  if (texture._depth>1 || texture._spectrum<_spectrum)
30414  throw CImgArgumentException(_cimg_instance
30415  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
30416  cimg_instance,
30417  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30418  if (light._depth>1 || light._spectrum<_spectrum)
30419  throw CImgArgumentException(_cimg_instance
30420  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
30421  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
30422  if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30423  if (is_overlapped(light)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30424  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30425  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30426  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd-1;
30427  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30428  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
30429  float
30430  ntx0 = tx0/z0, nty0 = ty0/z0,
30431  ntx1 = tx1/z1, nty1 = ty1/z1,
30432  ntx2 = tx2/z2, nty2 = ty2/z2,
30433  nz0 = 1/z0, nz1 = 1/z1, nz2 = 1/z2;
30434  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
30435  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
30436  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
30437  if (ny0>=height() || ny2<0) return *this;
30438  float
30439  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
30440  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
30441  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
30442  ptyl = (nty1 - nty0)/(ny1 - ny0),
30443  ptyr = (nty2 - nty0)/(ny2 - ny0),
30444  ptyn = (nty2 - nty1)/(ny2 - ny1),
30445  pzl = (nz1 - nz0)/(ny1 - ny0),
30446  pzr = (nz2 - nz0)/(ny2 - ny0),
30447  pzn = (nz2 - nz1)/(ny2 - ny1),
30448  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
30449  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
30450  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
30451  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1))),
30452  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
30453  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
30454  _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
30455  nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
30456  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
30457  int
30458  xleft = xleft0, xright = xright0,
30459  lxleft = lxleft0, lxright = lxright0,
30460  lyleft = lyleft0, lyright = lyright0;
30461  float
30462  zleft = zl, zright = zr,
30463  txleft = txl, txright = txr,
30464  tyleft = tyl, tyright = tyr;
30465  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
30466  const int
30467  dx = xright - xleft,
30468  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
30469  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
30470  rlx = dx?(lxright - lxleft)/dx:0,
30471  rly = dx?(lyright - lyleft)/dx:0,
30472  slx = lxright>lxleft?1:-1,
30473  sly = lyright>lyleft?1:-1,
30474  ndlx = dlx - (dx?dx*(dlx/dx):0),
30475  ndly = dly - (dx?dx*(dly/dx):0);
30476  const float
30477  pentez = (zright - zleft)/dx,
30478  pentetx = (txright - txleft)/dx,
30479  pentety = (tyright - tyleft)/dx;
30480  int errlx = dx>>1, errly = errlx;
30481  if (xleft<0 && dx) {
30482  zleft-=xleft*(zright - zleft)/dx;
30483  lxleft-=xleft*(lxright - lxleft)/dx;
30484  lyleft-=xleft*(lyright - lyleft)/dx;
30485  txleft-=xleft*(txright - txleft)/dx;
30486  tyleft-=xleft*(tyright - tyleft)/dx;
30487  }
30488  if (xleft<0) xleft = 0;
30489  if (xright>=width()-1) xright = width()-1;
30490  T* ptrd = data(xleft,y,0,0);
30491  if (opacity>=1) for (int x = xleft; x<=xright; ++x) {
30492  const float invz = 1/zleft;
30493  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30494  cimg_forC(*this,c) {
30495  const tl l = light(lxleft,lyleft,c);
30496  *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30497  ptrd+=whd; col+=twhd;
30498  }
30499  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30500  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30501  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30502  } else for (int x = xleft; x<=xright; ++x) {
30503  const float invz = 1/zleft;
30504  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30505  cimg_forC(*this,c) {
30506  const tl l = light(lxleft,lyleft,c);
30507  const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30508  *ptrd = (T)(nopacity*val + *ptrd*copacity);
30509  ptrd+=whd; col+=twhd;
30510  }
30511  ptrd-=offx; zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30512  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30513  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30514  }
30515  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
30516  }
30517  return *this;
30518  }
30519 
30521  template<typename tz, typename tc, typename tl>
30523  const int x0, const int y0, const float z0,
30524  const int x1, const int y1, const float z1,
30525  const int x2, const int y2, const float z2,
30526  const CImg<tc>& texture,
30527  const int tx0, const int ty0,
30528  const int tx1, const int ty1,
30529  const int tx2, const int ty2,
30530  const CImg<tl>& light,
30531  const int lx0, const int ly0,
30532  const int lx1, const int ly1,
30533  const int lx2, const int ly2,
30534  const float opacity=1) {
30535  typedef typename cimg::superset<tz,float>::type tzfloat;
30536  if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
30537  if (!is_sameXY(zbuffer))
30538  throw CImgArgumentException(_cimg_instance
30539  "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have different dimensions.",
30540  cimg_instance,
30541  zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
30542  if (texture._depth>1 || texture._spectrum<_spectrum)
30543  throw CImgArgumentException(_cimg_instance
30544  "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
30545  cimg_instance,
30546  texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
30547  if (light._depth>1 || light._spectrum<_spectrum)
30548  throw CImgArgumentException(_cimg_instance
30549  "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
30550  cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
30551  if (is_overlapped(texture)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
30552  +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30553  if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
30554  texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
30555  static const T maxval = (T)cimg::min(cimg::type<T>::max(),cimg::type<tc>::max());
30556  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30557  const long whd = (long)_width*_height*_depth, twhd = (long)texture._width*texture._height*texture._depth, offx = _spectrum*whd;
30558  int nx0 = x0, ny0 = y0, nx1 = x1, ny1 = y1, nx2 = x2, ny2 = y2,
30559  nlx0 = lx0, nly0 = ly0, nlx1 = lx1, nly1 = ly1, nlx2 = lx2, nly2 = ly2;
30560  float
30561  ntx0 = tx0/z0, nty0 = ty0/z0,
30562  ntx1 = tx1/z1, nty1 = ty1/z1,
30563  ntx2 = tx2/z2, nty2 = ty2/z2;
30564  tzfloat nz0 = 1/(tzfloat)z0, nz1 = 1/(tzfloat)z1, nz2 = 1/(tzfloat)z2;
30565  if (ny0>ny1) cimg::swap(nx0,nx1,ny0,ny1,ntx0,ntx1,nty0,nty1,nlx0,nlx1,nly0,nly1,nz0,nz1);
30566  if (ny0>ny2) cimg::swap(nx0,nx2,ny0,ny2,ntx0,ntx2,nty0,nty2,nlx0,nlx2,nly0,nly2,nz0,nz2);
30567  if (ny1>ny2) cimg::swap(nx1,nx2,ny1,ny2,ntx1,ntx2,nty1,nty2,nlx1,nlx2,nly1,nly2,nz1,nz2);
30568  if (ny0>=height() || ny2<0) return *this;
30569  float
30570  ptxl = (ntx1 - ntx0)/(ny1 - ny0),
30571  ptxr = (ntx2 - ntx0)/(ny2 - ny0),
30572  ptxn = (ntx2 - ntx1)/(ny2 - ny1),
30573  ptyl = (nty1 - nty0)/(ny1 - ny0),
30574  ptyr = (nty2 - nty0)/(ny2 - ny0),
30575  ptyn = (nty2 - nty1)/(ny2 - ny1),
30576  txr = ny0>=0?ntx0:(ntx0 - ny0*(ntx2 - ntx0)/(ny2 - ny0)),
30577  tyr = ny0>=0?nty0:(nty0 - ny0*(nty2 - nty0)/(ny2 - ny0)),
30578  txl = ny1>=0?(ny0>=0?ntx0:(ntx0 - ny0*(ntx1 - ntx0)/(ny1 - ny0))):(ptxl=ptxn,(ntx1 - ny1*(ntx2 - ntx1)/(ny2 - ny1))),
30579  tyl = ny1>=0?(ny0>=0?nty0:(nty0 - ny0*(nty1 - nty0)/(ny1 - ny0))):(ptyl=ptyn,(nty1 - ny1*(nty2 - nty1)/(ny2 - ny1)));
30580  tzfloat
30581  pzl = (nz1 - nz0)/(ny1 - ny0),
30582  pzr = (nz2 - nz0)/(ny2 - ny0),
30583  pzn = (nz2 - nz1)/(ny2 - ny1),
30584  zr = ny0>=0?nz0:(nz0 - ny0*(nz2 - nz0)/(ny2 - ny0)),
30585  zl = ny1>=0?(ny0>=0?nz0:(nz0 - ny0*(nz1 - nz0)/(ny1 - ny0))):(pzl=pzn,(nz1 - ny1*(nz2 - nz1)/(ny2 - ny1)));
30586  _cimg_for_triangle3(*this,xleft0,lxleft0,lyleft0,xright0,lxright0,lyright0,y,
30587  nx0,ny0,nlx0,nly0,nx1,ny1,nlx1,nly1,nx2,ny2,nlx2,nly2) {
30588  if (y==ny1) { zl = nz1; txl = ntx1; tyl = nty1; pzl = pzn; ptxl = ptxn; ptyl = ptyn; }
30589  int
30590  xleft = xleft0, xright = xright0,
30591  lxleft = lxleft0, lxright = lxright0,
30592  lyleft = lyleft0, lyright = lyright0;
30593  float txleft = txl, txright = txr, tyleft = tyl, tyright = tyr;
30594  tzfloat zleft = zl, zright = zr;
30595  if (xright<xleft) cimg::swap(xleft,xright,zleft,zright,txleft,txright,tyleft,tyright,lxleft,lxright,lyleft,lyright);
30596  const int
30597  dx = xright - xleft,
30598  dlx = lxright>lxleft?lxright - lxleft:lxleft - lxright,
30599  dly = lyright>lyleft?lyright - lyleft:lyleft - lyright,
30600  rlx = dx?(lxright - lxleft)/dx:0,
30601  rly = dx?(lyright - lyleft)/dx:0,
30602  slx = lxright>lxleft?1:-1,
30603  sly = lyright>lyleft?1:-1,
30604  ndlx = dlx - (dx?dx*(dlx/dx):0),
30605  ndly = dly - (dx?dx*(dly/dx):0);
30606  float pentetx = (txright - txleft)/dx, pentety = (tyright - tyleft)/dx;
30607  const tzfloat pentez = (zright - zleft)/dx;
30608  int errlx = dx>>1, errly = errlx;
30609  if (xleft<0 && dx) {
30610  zleft-=xleft*(zright - zleft)/dx;
30611  lxleft-=xleft*(lxright - lxleft)/dx;
30612  lyleft-=xleft*(lyright - lyleft)/dx;
30613  txleft-=xleft*(txright - txleft)/dx;
30614  tyleft-=xleft*(tyright - tyleft)/dx;
30615  }
30616  if (xleft<0) xleft = 0;
30617  if (xright>=width()-1) xright = width()-1;
30618  T* ptrd = data(xleft,y);
30619  tz *ptrz = zbuffer.data(xleft,y);
30620  if (opacity>=1) for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
30621  if (zleft>=(tzfloat)*ptrz) {
30622  *ptrz = (tz)zleft;
30623  const tzfloat invz = 1/zleft;
30624  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30625  cimg_forC(*this,c) {
30626  const tl l = light(lxleft,lyleft,c);
30627  *ptrd = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30628  ptrd+=whd; col+=twhd;
30629  }
30630  ptrd-=offx;
30631  }
30632  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30633  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30634  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30635  } else for (int x = xleft; x<=xright; ++x, ++ptrz, ++ptrd) {
30636  if (zleft>=(tzfloat)*ptrz) {
30637  *ptrz = (tz)zleft;
30638  const tzfloat invz = 1/zleft;
30639  const tc *col = texture.data((int)(txleft*invz),(int)(tyleft*invz));
30640  cimg_forC(*this,c) {
30641  const tl l = light(lxleft,lyleft,c);
30642  const T val = (T)(l<1?l**col:(2-l)**col+(l-1)*maxval);
30643  *ptrd = (T)(nopacity*val + *ptrd*copacity);
30644  ptrd+=whd; col+=twhd;
30645  }
30646  ptrd-=offx;
30647  }
30648  zleft+=pentez; txleft+=pentetx; tyleft+=pentety;
30649  lxleft+=rlx+((errlx-=ndlx)<0?errlx+=dx,slx:0);
30650  lyleft+=rly+((errly-=ndly)<0?errly+=dx,sly:0);
30651  }
30652  zr+=pzr; txr+=ptxr; tyr+=ptyr; zl+=pzl; txl+=ptxl; tyl+=ptyl;
30653  }
30654  return *this;
30655  }
30656 
30658 
30670  CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
30671  const int x1, const int y1, const int z1, const int c1,
30672  const T val, const float opacity=1) {
30673  if (is_empty()) return *this;
30674  const bool bx = (x0<x1), by = (y0<y1), bz = (z0<z1), bc = (c0<c1);
30675  const int
30676  nx0 = bx?x0:x1, nx1 = bx?x1:x0,
30677  ny0 = by?y0:y1, ny1 = by?y1:y0,
30678  nz0 = bz?z0:z1, nz1 = bz?z1:z0,
30679  nc0 = bc?c0:c1, nc1 = bc?c1:c0;
30680  const int
30681  lX = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
30682  lY = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
30683  lZ = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
30684  lC = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
30685  const unsigned long
30686  offX = (unsigned long)_width - lX,
30687  offY = (unsigned long)_width*(_height - lY),
30688  offZ = (unsigned long)_width*_height*(_depth - lZ);
30689  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
30690  T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
30691  if (lX>0 && lY>0 && lZ>0 && lC>0)
30692  for (int v = 0; v<lC; ++v) {
30693  for (int z = 0; z<lZ; ++z) {
30694  for (int y = 0; y<lY; ++y) {
30695  if (opacity>=1) {
30696  if (sizeof(T)!=1) { for (int x = 0; x<lX; ++x) *(ptrd++) = val; ptrd+=offX; }
30697  else { std::memset(ptrd,(int)val,lX); ptrd+=_width; }
30698  } else { for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
30699  }
30700  ptrd+=offY;
30701  }
30702  ptrd+=offZ;
30703  }
30704  return *this;
30705  }
30706 
30708 
30718  template<typename tc>
30719  CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
30720  const int x1, const int y1, const int z1,
30721  const tc *const color, const float opacity=1) {
30722  if (is_empty()) return *this;
30723  if (!color)
30724  throw CImgArgumentException(_cimg_instance
30725  "draw_rectangle(): Specified color is (null).",
30726  cimg_instance);
30727  cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
30728  return *this;
30729  }
30730 
30732  template<typename tc>
30733  CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
30734  const int x1, const int y1, const int z1,
30735  const tc *const color, const float opacity,
30736  const unsigned int pattern) {
30737  return draw_line(x0,y0,z0,x1,y0,z0,color,opacity,pattern,true).
30738  draw_line(x1,y0,z0,x1,y1,z0,color,opacity,pattern,false).
30739  draw_line(x1,y1,z0,x0,y1,z0,color,opacity,pattern,false).
30740  draw_line(x0,y1,z0,x0,y0,z0,color,opacity,pattern,false).
30741  draw_line(x0,y0,z1,x1,y0,z1,color,opacity,pattern,true).
30742  draw_line(x1,y0,z1,x1,y1,z1,color,opacity,pattern,false).
30743  draw_line(x1,y1,z1,x0,y1,z1,color,opacity,pattern,false).
30744  draw_line(x0,y1,z1,x0,y0,z1,color,opacity,pattern,false).
30745  draw_line(x0,y0,z0,x0,y0,z1,color,opacity,pattern,true).
30746  draw_line(x1,y0,z0,x1,y0,z1,color,opacity,pattern,true).
30747  draw_line(x1,y1,z0,x1,y1,z1,color,opacity,pattern,true).
30748  draw_line(x0,y1,z0,x0,y1,z1,color,opacity,pattern,true);
30749  }
30750 
30752 
30760  template<typename tc>
30761  CImg<T>& draw_rectangle(const int x0, const int y0,
30762  const int x1, const int y1,
30763  const tc *const color, const float opacity=1) {
30764  return draw_rectangle(x0,y0,0,x1,y1,_depth-1,color,opacity);
30765  }
30766 
30768  template<typename tc>
30769  CImg<T>& draw_rectangle(const int x0, const int y0,
30770  const int x1, const int y1,
30771  const tc *const color, const float opacity,
30772  const unsigned int pattern) {
30773  if (is_empty()) return *this;
30774  if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
30775  if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
30776  const bool bx = (x0<x1), by = (y0<y1);
30777  const int
30778  nx0 = bx?x0:x1, nx1 = bx?x1:x0,
30779  ny0 = by?y0:y1, ny1 = by?y1:y0;
30780  if (ny1==ny0+1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
30781  draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
30782  return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
30783  draw_line(nx1,ny0+1,nx1,ny1-1,color,opacity,pattern,false).
30784  draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
30785  draw_line(nx0,ny1-1,nx0,ny0+1,color,opacity,pattern,false);
30786  }
30787 
30789 
30794  template<typename t, typename tc>
30795  CImg<T>& draw_polygon(const CImg<t>& points,
30796  const tc *const color, const float opacity=1) {
30797  if (is_empty() || !points || points._width<3) return *this;
30798  if (!color)
30799  throw CImgArgumentException(_cimg_instance
30800  "draw_polygon(): Specified color is (null).",
30801  cimg_instance);
30802 
30803  // Normalize 2d input coordinates.
30804  CImg<intT> npoints(points._width,2);
30805  int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
30806  unsigned int nb_points = 1;
30807  for (unsigned int p = 1; p<points._width; ++p) {
30808  const int nx = (int)points(p,0), ny = (int)points(p,1);
30809  if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
30810  }
30811 
30812  if (nb_points==3) return draw_triangle((int)npoints(0,0),(int)npoints(0,1),
30813  (int)npoints(1,0),(int)npoints(1,1),
30814  (int)npoints(2,0),(int)npoints(2,1),color,opacity);
30815  // Draw polygon segments.
30816  _draw_scanline(color,opacity);
30817  int
30818  xmax = 0, xmin = (int)npoints.get_shared_points(0,nb_points-1,0).min_max(xmax),
30819  ymax = 0, ymin = (int)npoints.get_shared_points(0,nb_points-1,1).min_max(ymax);
30820  if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
30821  if (ymin==ymax) return _draw_scanline(xmin,xmax,ymin,color,opacity);
30822  const unsigned int
30823  nymin = ymin<0?0:(unsigned int)ymin,
30824  nymax = ymax>=height()?_height-1:(unsigned int)ymax,
30825  dy = 1 + nymax - nymin;
30826  CImg<intT> X(1+2*nb_points,dy,1,1,0), tmp;
30827  int cx = (int)npoints(0,0), cy = (int)npoints(0,1);
30828  unsigned int cp = 0;
30829  for (unsigned int p = 0; p<nb_points; ++p) {
30830  const unsigned int np = (p!=nb_points-1)?p+1:0, ap = (np!=nb_points-1)?np+1:0;
30831  const int
30832  nx = (int)npoints(np,0), ny = (int)npoints(np,1), ay = (int)npoints(ap,1),
30833  y0 = cy - nymin, y1 = ny - nymin;
30834  if (y0!=y1) {
30835  const int countermin = ((ny<ay && cy<ny) || (ny>ay && cy>ny))?1:0;
30836  for (int x = cx, y = y0, _sx = 1, _sy = 1,
30837  _dx = nx>cx?nx-cx:((_sx=-1),cx-nx),
30838  _dy = y1>y0?y1-y0:((_sy=-1),y0-y1),
30839  _counter = ((_dx-=_dy?_dy*(_dx/_dy):0),_dy),
30840  _err = _dx>>1,
30841  _rx = _dy?(nx-cx)/_dy:0;
30842  _counter>=countermin;
30843  --_counter, y+=_sy, x+=_rx + ((_err-=_dx)<0?_err+=_dy,_sx:0))
30844  if (y>=0 && y<(int)dy) X(++X(0,y),y) = x;
30845  cp = np; cx = nx; cy = ny;
30846  } else {
30847  const int pp = (cp?cp-1:nb_points-1), py = (int)npoints(pp,1);
30848  if (y0>=0 && y0<(int)dy && (!p || (cy>py && ay>cy) || (cy<py && ay<cy))) X(++X(0,y0),y0) = nx;
30849  if (cy!=ay) { cp = np; cx = nx; cy = ny; }
30850  }
30851  }
30852 
30853  // Draw polygon scanlines.
30854  for (int y = 0; y<(int)dy; ++y) {
30855  tmp.assign(X.data(1,y),X(0,y),1,1,1,true).sort();
30856  for (int i = 1; i<=X(0,y); ) {
30857  const int xb = X(i++,y), xe = X(i++,y);
30858  _draw_scanline(xb,xe,nymin+y,color,opacity);
30859  }
30860  }
30861 
30862  return *this;
30863  }
30864 
30866  template<typename t, typename tc>
30867  CImg<T>& draw_polygon(const CImg<t>& points,
30868  const tc *const color, const float opacity, const unsigned int pattern) {
30869  if (is_empty() || !points || points._width<3) return *this;
30870  bool ninit_hatch = true;
30871  switch (points._height) {
30872  case 0 : case 1 :
30873  throw CImgArgumentException(_cimg_instance
30874  "draw_polygon(): Invalid specified point set.",
30875  cimg_instance);
30876  case 2 : { // 2d version.
30877  CImg<intT> npoints(points._width,2);
30878  int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1);
30879  unsigned int nb_points = 1;
30880  for (unsigned int p = 1; p<points._width; ++p) {
30881  const int nx = (int)points(p,0), ny = (int)points(p,1);
30882  if (nx!=x || ny!=y) { npoints(nb_points,0) = nx; npoints(nb_points++,1) = ny; x = nx; y = ny; }
30883  }
30884  const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1);
30885  int ox = x0, oy = y0;
30886  for (unsigned int i = 1; i<nb_points; ++i) {
30887  const int x = (int)npoints(i,0), y = (int)npoints(i,1);
30888  draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
30889  ninit_hatch = false;
30890  ox = x; oy = y;
30891  }
30892  draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
30893  } break;
30894  default : { // 3d version.
30895  CImg<intT> npoints(points._width,3);
30896  int x = npoints(0,0) = (int)points(0,0), y = npoints(0,1) = (int)points(0,1), z = npoints(0,2) = (int)points(0,2);
30897  unsigned int nb_points = 1;
30898  for (unsigned int p = 1; p<points._width; ++p) {
30899  const int nx = (int)points(p,0), ny = (int)points(p,1), nz = (int)points(p,2);
30900  if (nx!=x || ny!=y || nz!=z) { npoints(nb_points,0) = nx; npoints(nb_points,1) = ny; npoints(nb_points++,2) = nz; x = nx; y = ny; z = nz; }
30901  }
30902  const int x0 = (int)npoints(0,0), y0 = (int)npoints(0,1), z0 = (int)npoints(0,2);
30903  int ox = x0, oy = y0, oz = z0;
30904  for (unsigned int i = 1; i<nb_points; ++i) {
30905  const int x = (int)npoints(i,0), y = (int)npoints(i,1), z = (int)npoints(i,2);
30906  draw_line(ox,oy,oz,x,y,z,color,opacity,pattern,ninit_hatch);
30907  ninit_hatch = false;
30908  ox = x; oy = y; oz = z;
30909  }
30910  draw_line(ox,oy,oz,x0,y0,z0,color,opacity,pattern,false);
30911  }
30912  }
30913  return *this;
30914  }
30915 
30917 
30926  template<typename tc>
30927  CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
30928  const tc *const color, const float opacity=1) {
30929  return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U);
30930  }
30931 
30933 
30940  template<typename t, typename tc>
30941  CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
30942  const tc *const color, const float opacity=1) {
30943  CImgList<t> eig = tensor.get_symmetric_eigen();
30944  const CImg<t> &val = eig[0], &vec = eig[1];
30945  return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
30946  std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
30947  color,opacity);
30948  }
30949 
30951 
30961  template<typename tc>
30962  CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
30963  const tc *const color, const float opacity, const unsigned int pattern) {
30964  if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern);
30965  return *this;
30966  }
30967 
30969 
30977  template<typename t, typename tc>
30978  CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
30979  const tc *const color, const float opacity,
30980  const unsigned int pattern) {
30981  CImgList<t> eig = tensor.get_symmetric_eigen();
30982  const CImg<t> &val = eig[0], &vec = eig[1];
30983  return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
30984  std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
30985  color,opacity,pattern);
30986  }
30987 
30988  template<typename tc>
30989  CImg<T>& _draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
30990  const tc *const color, const float opacity,
30991  const unsigned int pattern) {
30992  if (is_empty()) return *this;
30993  if (!color)
30994  throw CImgArgumentException(_cimg_instance
30995  "draw_ellipse(): Specified color is (null).",
30996  cimg_instance);
30997  if (r1<=0 || r2<=0) return draw_point(x0,y0,color,opacity);
30998  _draw_scanline(color,opacity);
30999  const float
31000  nr1 = cimg::abs(r1), nr2 = cimg::abs(r2),
31001  nangle = (float)(angle*cimg::PI/180),
31002  u = (float)std::cos(nangle),
31003  v = (float)std::sin(nangle),
31004  rmax = cimg::max(nr1,nr2),
31005  l1 = (float)std::pow(rmax/(nr1>0?nr1:1e-6),2),
31006  l2 = (float)std::pow(rmax/(nr2>0?nr2:1e-6),2),
31007  a = l1*u*u + l2*v*v,
31008  b = u*v*(l1-l2),
31009  c = l1*v*v + l2*u*u;
31010  const int
31011  yb = (int)std::sqrt(a*rmax*rmax/(a*c - b*b)),
31012  tymin = y0 - yb - 1,
31013  tymax = y0 + yb + 1,
31014  ymin = tymin<0?0:tymin,
31015  ymax = tymax>=height()?height()-1:tymax;
31016  int oxmin = 0, oxmax = 0;
31017  bool first_line = true;
31018  for (int y = ymin; y<=ymax; ++y) {
31019  const float
31020  Y = y - y0 + (y<y0?0.5f:-0.5f),
31021  delta = b*b*Y*Y - a*(c*Y*Y - rmax*rmax),
31022  sdelta = delta>0?(float)std::sqrt(delta)/a:0.0f,
31023  bY = b*Y/a,
31024  fxmin = x0 - 0.5f - bY - sdelta,
31025  fxmax = x0 + 0.5f - bY + sdelta;
31026  const int xmin = (int)fxmin, xmax = (int)fxmax;
31027  if (!pattern) _draw_scanline(xmin,xmax,y,color,opacity);
31028  else {
31029  if (first_line) {
31030  if (y0-yb>=0) _draw_scanline(xmin,xmax,y,color,opacity);
31031  else draw_point(xmin,y,color,opacity).draw_point(xmax,y,color,opacity);
31032  first_line = false;
31033  } else {
31034  if (xmin<oxmin) _draw_scanline(xmin,oxmin-1,y,color,opacity);
31035  else _draw_scanline(oxmin+(oxmin==xmin?0:1),xmin,y,color,opacity);
31036  if (xmax<oxmax) _draw_scanline(xmax,oxmax-1,y,color,opacity);
31037  else _draw_scanline(oxmax+(oxmax==xmax?0:1),xmax,y,color,opacity);
31038  if (y==tymax) _draw_scanline(xmin+1,xmax-1,y,color,opacity);
31039  }
31040  }
31041  oxmin = xmin; oxmax = xmax;
31042  }
31043  return *this;
31044  }
31045 
31047 
31056  template<typename tc>
31057  CImg<T>& draw_circle(const int x0, const int y0, int radius,
31058  const tc *const color, const float opacity=1) {
31059  if (is_empty()) return *this;
31060  if (!color)
31061  throw CImgArgumentException(_cimg_instance
31062  "draw_circle(): Specified color is (null).",
31063  cimg_instance);
31064  _draw_scanline(color,opacity);
31065  if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
31066  if (y0>=0 && y0<height()) _draw_scanline(x0-radius,x0+radius,y0,color,opacity);
31067  for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
31068  if (f>=0) {
31069  const int x1 = x0-x, x2 = x0+x, y1 = y0-y, y2 = y0+y;
31070  if (y1>=0 && y1<height()) _draw_scanline(x1,x2,y1,color,opacity);
31071  if (y2>=0 && y2<height()) _draw_scanline(x1,x2,y2,color,opacity);
31072  f+=(ddFy+=2); --y;
31073  }
31074  const bool no_diag = y!=(x++);
31075  ++(f+=(ddFx+=2));
31076  const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x;
31077  if (no_diag) {
31078  if (y1>=0 && y1<height()) _draw_scanline(x1,x2,y1,color,opacity);
31079  if (y2>=0 && y2<height()) _draw_scanline(x1,x2,y2,color,opacity);
31080  }
31081  }
31082  return *this;
31083  }
31084 
31086 
31094  template<typename tc>
31095  CImg<T>& draw_circle(const int x0, const int y0, int radius,
31096  const tc *const color, const float opacity,
31097  const unsigned int pattern) {
31098  cimg::unused(pattern);
31099  if (is_empty()) return *this;
31100  if (!color)
31101  throw CImgArgumentException(_cimg_instance
31102  "draw_circle(): Specified color is (null).",
31103  cimg_instance);
31104  if (radius<0 || x0-radius>=width() || y0+radius<0 || y0-radius>=height()) return *this;
31105  if (!radius) return draw_point(x0,y0,color,opacity);
31106  draw_point(x0-radius,y0,color,opacity).draw_point(x0+radius,y0,color,opacity).
31107  draw_point(x0,y0-radius,color,opacity).draw_point(x0,y0+radius,color,opacity);
31108  if (radius==1) return *this;
31109  for (int f = 1-radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
31110  if (f>=0) { f+=(ddFy+=2); --y; }
31111  ++x; ++(f+=(ddFx+=2));
31112  if (x!=y+1) {
31113  const int x1 = x0-y, x2 = x0+y, y1 = y0-x, y2 = y0+x, x3 = x0-x, x4 = x0+x, y3 = y0-y, y4 = y0+y;
31114  draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
31115  draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
31116  if (x!=y)
31117  draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
31118  draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
31119  }
31120  }
31121  return *this;
31122  }
31123 
31125 
31133  template<typename t>
31134  CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
31135  const CImg<t>& sprite, const float opacity=1) {
31136  if (is_empty() || !sprite) return *this;
31137  if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
31138  if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1) return assign(sprite,false);
31139  const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
31140  const int
31141  lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
31142  lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
31143  lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
31144  lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
31145  const t
31146  *ptrs = sprite._data -
31147  (bx?x0:0) -
31148  (by?y0*sprite.width():0) -
31149  (bz?z0*sprite.width()*sprite.height():0) -
31150  (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
31151  const unsigned long
31152  offX = (unsigned long)_width - lX,
31153  soffX = (unsigned long)sprite._width - lX,
31154  offY = (unsigned long)_width*(_height - lY),
31155  soffY = (unsigned long)sprite._width*(sprite._height - lY),
31156  offZ = (unsigned long)_width*_height*(_depth - lZ),
31157  soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ);
31158  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31159  if (lX>0 && lY>0 && lZ>0 && lC>0) {
31160  T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
31161  for (int v = 0; v<lC; ++v) {
31162  for (int z = 0; z<lZ; ++z) {
31163  for (int y = 0; y<lY; ++y) {
31164  if (opacity>=1) for (int x = 0; x<lX; ++x) *(ptrd++) = (T)*(ptrs++);
31165  else for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
31166  ptrd+=offX; ptrs+=soffX;
31167  }
31168  ptrd+=offY; ptrs+=soffY;
31169  }
31170  ptrd+=offZ; ptrs+=soffZ;
31171  }
31172  }
31173  return *this;
31174  }
31175 
31177  CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
31178  const CImg<T>& sprite, const float opacity=1) {
31179  if (is_empty() || !sprite) return *this;
31180  if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
31181  if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1) return assign(sprite,false);
31182  const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
31183  const int
31184  lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
31185  lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
31186  lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
31187  lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
31188  const T
31189  *ptrs = sprite._data -
31190  (bx?x0:0) -
31191  (by?y0*sprite.width():0) -
31192  (bz?z0*sprite.width()*sprite.height():0) -
31193  (bc?c0*sprite.width()*sprite.height()*sprite.depth():0);
31194  const unsigned long
31195  offX = (unsigned long)_width - lX,
31196  soffX = (unsigned long)sprite._width - lX,
31197  offY = (unsigned long)_width*(_height - lY),
31198  soffY = (unsigned long)sprite._width*(sprite._height - lY),
31199  offZ = (unsigned long)_width*_height*(_depth - lZ),
31200  soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ),
31201  slX = lX*sizeof(T);
31202  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
31203  if (lX>0 && lY>0 && lZ>0 && lC>0) {
31204  T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
31205  for (int v = 0; v<lC; ++v) {
31206  for (int z = 0; z<lZ; ++z) {
31207  if (opacity>=1) for (int y = 0; y<lY; ++y) { std::memcpy(ptrd,ptrs,slX); ptrd+=_width; ptrs+=sprite._width; }
31208  else for (int y = 0; y<lY; ++y) {
31209  for (int x = 0; x<lX; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
31210  ptrd+=offX; ptrs+=soffX;
31211  }
31212  ptrd+=offY; ptrs+=soffY;
31213  }
31214  ptrd+=offZ; ptrs+=soffZ;
31215  }
31216  }
31217  return *this;
31218  }
31219 
31221  template<typename t>
31222  CImg<T>& draw_image(const int x0, const int y0, const int z0,
31223  const CImg<t>& sprite, const float opacity=1) {
31224  return draw_image(x0,y0,z0,0,sprite,opacity);
31225  }
31226 
31228  template<typename t>
31229  CImg<T>& draw_image(const int x0, const int y0,
31230  const CImg<t>& sprite, const float opacity=1) {
31231  return draw_image(x0,y0,0,sprite,opacity);
31232  }
31233 
31235  template<typename t>
31236  CImg<T>& draw_image(const int x0,
31237  const CImg<t>& sprite, const float opacity=1) {
31238  return draw_image(x0,0,sprite,opacity);
31239  }
31240 
31242  template<typename t>
31243  CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
31244  return draw_image(0,sprite,opacity);
31245  }
31246 
31248 
31261  template<typename ti, typename tm>
31262  CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
31263  const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31264  const float mask_max_value=1) {
31265  if (is_empty() || !sprite || !mask) return *this;
31266  if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
31267  if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
31268  if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
31269  throw CImgArgumentException(_cimg_instance
31270  "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have incompatible dimensions.",
31271  cimg_instance,
31272  sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
31273  mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
31274 
31275  const bool bx = (x0<0), by = (y0<0), bz = (z0<0), bc = (c0<0);
31276  const int
31277  lX = sprite.width() - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0) + (bx?x0:0),
31278  lY = sprite.height() - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0) + (by?y0:0),
31279  lZ = sprite.depth() - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0) + (bz?z0:0),
31280  lC = sprite.spectrum() - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0) + (bc?c0:0);
31281  const int
31282  coff = -(bx?x0:0)-(by?y0*mask.width():0)-(bz?z0*mask.width()*mask.height():0)-(bc?c0*mask.width()*mask.height()*mask.depth():0),
31283  ssize = mask.width()*mask.height()*mask.depth();
31284  const ti *ptrs = sprite._data + coff;
31285  const tm *ptrm = mask._data + coff;
31286  const unsigned long
31287  offX = (unsigned long)_width - lX,
31288  soffX = (unsigned long)sprite._width - lX,
31289  offY = (unsigned long)_width*(_height - lY),
31290  soffY = (unsigned long)sprite._width*(sprite._height - lY),
31291  offZ = (unsigned long)_width*_height*(_depth - lZ),
31292  soffZ = (unsigned long)sprite._width*sprite._height*(sprite._depth - lZ);
31293  if (lX>0 && lY>0 && lZ>0 && lC>0) {
31294  T *ptrd = data(x0<0?0:x0,y0<0?0:y0,z0<0?0:z0,c0<0?0:c0);
31295  for (int c = 0; c<lC; ++c) {
31296  ptrm = mask._data + (ptrm - mask._data)%ssize;
31297  for (int z = 0; z<lZ; ++z) {
31298  for (int y = 0; y<lY; ++y) {
31299  for (int x = 0; x<lX; ++x) {
31300  const float mopacity = (float)(*(ptrm++)*opacity),
31301  nopacity = cimg::abs(mopacity), copacity = mask_max_value - cimg::max(mopacity,0);
31302  *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
31303  ++ptrd;
31304  }
31305  ptrd+=offX; ptrs+=soffX; ptrm+=soffX;
31306  }
31307  ptrd+=offY; ptrs+=soffY; ptrm+=soffY;
31308  }
31309  ptrd+=offZ; ptrs+=soffZ; ptrm+=soffZ;
31310  }
31311  }
31312  return *this;
31313  }
31314 
31316  template<typename ti, typename tm>
31317  CImg<T>& draw_image(const int x0, const int y0, const int z0,
31318  const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31319  const float mask_max_value=1) {
31320  return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
31321  }
31322 
31324  template<typename ti, typename tm>
31325  CImg<T>& draw_image(const int x0, const int y0,
31326  const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31327  const float mask_max_value=1) {
31328  return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
31329  }
31330 
31332  template<typename ti, typename tm>
31333  CImg<T>& draw_image(const int x0,
31334  const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31335  const float mask_max_value=1) {
31336  return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
31337  }
31338 
31340  template<typename ti, typename tm>
31341  CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
31342  const float mask_max_value=1) {
31343  return draw_image(0,sprite,mask,opacity,mask_max_value);
31344  }
31345 
31347 
31356  template<typename tc1, typename tc2, typename t>
31357  CImg<T>& draw_text(const int x0, const int y0,
31358  const char *const text,
31359  const tc1 *const foreground_color, const tc2 *const background_color,
31360  const float opacity, const CImgList<t>& font, ...) {
31361  if (!font) return *this;
31362  char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
31363  cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31364  return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font);
31365  }
31366 
31368 
31371  template<typename tc, typename t>
31372  CImg<T>& draw_text(const int x0, const int y0,
31373  const char *const text,
31374  const tc *const foreground_color, const int,
31375  const float opacity, const CImgList<t>& font, ...) {
31376  if (!font) return *this;
31377  char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
31378  cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31379  return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,font);
31380  }
31381 
31383 
31386  template<typename tc, typename t>
31387  CImg<T>& draw_text(const int x0, const int y0,
31388  const char *const text,
31389  const int, const tc *const background_color,
31390  const float opacity, const CImgList<t>& font, ...) {
31391  if (!font) return *this;
31392  char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font);
31393  cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31394  return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,font);
31395  }
31396 
31398 
31407  template<typename tc1, typename tc2>
31408  CImg<T>& draw_text(const int x0, const int y0,
31409  const char *const text,
31410  const tc1 *const foreground_color, const tc2 *const background_color,
31411  const float opacity=1, const unsigned int font_height=13, ...) {
31412  if (!font_height) return *this;
31413  char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31414  static CImgList<floatT> font;
31415  const unsigned int
31416  ref_height = font_height<=13?13:font_height<=28?24:font_height<=32?32:57,
31417  padding_x = font_height<=18?1:font_height<=32?2:3;
31418  if (!font || font[0]._height!=font_height) {
31419  font = CImgList<floatT>::font(ref_height,true);
31420  font[0].assign(1,font_height);
31421  if (ref_height==font_height) cimglist_for(font,l) font[l].resize(font[l]._width + padding_x,-100,-100,-100,0);
31422  }
31423  if (is_empty()) {
31424  if (font[0]._spectrum!=1) cimglist_for_in(font,0,255,l) font[l].channel(0);
31425  } else if (font[0]._spectrum<_spectrum) cimglist_for_in(font,0,255,l) font[l].resize(-100,-100,1,_spectrum);
31426  if (ref_height!=font_height) for (const char *ptrs = tmp; *ptrs; ++ptrs) {
31427  const unsigned int __c = (unsigned int)(unsigned char)*ptrs, _c = (__c=='\t')?' ':__c;
31428  if (_c<font._width) {
31429  CImg<floatT> &c = font[_c];
31430  if (c._height!=font_height) {
31431  c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3);
31432  c.resize(c._width + padding_x,-100,-100,-100,0);
31433  }
31434  }
31435  if (_c+256U<font._width) {
31436  CImg<floatT> &c = font[_c+256];
31437  if (c._height!=font_height) {
31438  c.resize(cimg::max(1U,c._width*font_height/c._height),font_height,-100,-100,c._height>font_height?2:3);
31439  c.resize(c._width + padding_x,-100,-100,-100,0);
31440  }
31441  }
31442  }
31443  return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font);
31444  }
31445 
31447  template<typename tc>
31448  CImg<T>& draw_text(const int x0, const int y0,
31449  const char *const text,
31450  const tc *const foreground_color, const int background_color=0,
31451  const float opacity=1, const unsigned int font_height=13, ...) {
31452  if (!font_height) return *this;
31453  cimg::unused(background_color);
31454  char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31455  return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp);
31456  }
31457 
31459  template<typename tc>
31460  CImg<T>& draw_text(const int x0, const int y0,
31461  const char *const text,
31462  const int, const tc *const background_color,
31463  const float opacity=1, const unsigned int font_height=13, ...) {
31464  if (!font_height) return *this;
31465  char tmp[2048] = { 0 }; std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,sizeof(tmp),text,ap); va_end(ap);
31466  return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp);
31467  }
31468 
31469  template<typename tc1, typename tc2, typename t>
31470  CImg<T>& _draw_text(const int x0, const int y0,
31471  const char *const text,
31472  const tc1 *const foreground_color, const tc2 *const background_color,
31473  const float opacity, const CImgList<t>& font) {
31474  if (!text) return *this;
31475  if (!font)
31476  throw CImgArgumentException(_cimg_instance
31477  "draw_text(): Empty specified font.",
31478  cimg_instance);
31479  const unsigned int text_length = (unsigned int)std::strlen(text);
31480  if (is_empty()) {
31481  // If needed, pre-compute necessary size of the image
31482  int x = 0, y = 0, w = 0;
31483  unsigned char c = 0;
31484  for (unsigned int i = 0; i<text_length; ++i) {
31485  c = text[i];
31486  switch (c) {
31487  case '\n' : y+=font[0]._height; if (x>w) w = x; x = 0; break;
31488  case '\t' : x+=4*font[' ']._width; break;
31489  default : if (c<font._width) x+=font[c]._width;
31490  }
31491  }
31492  if (x!=0 || c=='\n') {
31493  if (x>w) w=x;
31494  y+=font[0]._height;
31495  }
31496  assign(x0+w,y0+y,1,font[0]._spectrum,0);
31497  if (background_color) cimg_forC(*this,c) get_shared_channel(c).fill((T)background_color[c]);
31498  }
31499 
31500  int x = x0, y = y0;
31501  CImg<t> letter;
31502  for (unsigned int i = 0; i<text_length; ++i) {
31503  const unsigned char c = text[i];
31504  switch (c) {
31505  case '\n' : y+=font[' ']._height; x = x0; break;
31506  case '\t' : x+=4*font[' ']._width; break;
31507  default : if (c<font._width) {
31508  letter = font[c];
31509  const unsigned int cmin = cimg::min(_spectrum,letter._spectrum);
31510  const CImg<t>& mask = (c+256)<(int)font._width?font[c+256]:font[c];
31511  if (foreground_color)
31512  for (unsigned long p = 0; p<(unsigned long)letter._width*letter._height; ++p)
31513  if (mask(p)) for (unsigned int c = 0; c<cmin; ++c) letter(p,0,0,c) = (t)(letter(p,0,0,c)*foreground_color[c]);
31514  if (background_color)
31515  for (unsigned long p = 0; p<(unsigned long)letter._width*letter._height; ++p)
31516  if (!mask(p)) for (unsigned int c = 0; c<cmin; ++c) letter(p,0,0,c) = (t)background_color[c];
31517  if (!background_color && font._width>=512) draw_image(x,y,letter,mask,opacity,(T)1);
31518  else draw_image(x,y,letter,opacity);
31519  x+=letter._width;
31520  }
31521  }
31522  }
31523  return *this;
31524  }
31525 
31527 
31537  template<typename t1, typename t2>
31539  const t2 *const color, const float opacity=1,
31540  const unsigned int sampling=25, const float factor=-20,
31541  const bool is_arrow=true, const unsigned int pattern=~0U) {
31542  return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
31543  }
31544 
31546 
31556  template<typename t1, typename t2>
31558  const CImg<t2>& color, const float opacity=1,
31559  const unsigned int sampling=25, const float factor=-20,
31560  const bool is_arrow=true, const unsigned int pattern=~0U) {
31561  if (is_empty()) return *this;
31562  if (!flow || flow._spectrum!=2)
31563  throw CImgArgumentException(_cimg_instance
31564  "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
31565  cimg_instance,
31566  flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
31567  if (sampling<=0)
31568  throw CImgArgumentException(_cimg_instance
31569  "draw_quiver(): Invalid sampling value %g "
31570  "(should be >0)",
31571  cimg_instance,
31572  sampling);
31573  const bool colorfield = (color._width==flow._width && color._height==flow._height && color._depth==1 && color._spectrum==_spectrum);
31574  if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
31575  float vmax,fact;
31576  if (factor<=0) {
31577  float m, M = (float)flow.get_norm(2).max_min(m);
31578  vmax = (float)cimg::max(cimg::abs(m),cimg::abs(M));
31579  if (!vmax) vmax = 1;
31580  fact = -factor;
31581  } else { fact = factor; vmax = 1; }
31582 
31583  for (unsigned int y = sampling/2; y<_height; y+=sampling)
31584  for (unsigned int x = sampling/2; x<_width; x+=sampling) {
31585  const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
31586  float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
31587  if (is_arrow) {
31588  const int xx = x+(int)u, yy = y+(int)v;
31589  if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.0f,pattern);
31590  else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.0f,pattern);
31591  } else {
31592  if (colorfield) draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),color.get_vector_at(X,Y)._data,opacity,pattern);
31593  else draw_line((int)(x-0.5*u),(int)(y-0.5*v),(int)(x+0.5*u),(int)(y+0.5*v),color._data,opacity,pattern);
31594  }
31595  }
31596 
31597  return *this;
31598  }
31599 
31601 
31610  template<typename t, typename tc>
31611  CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
31612  const tc *const color, const float opacity=1,
31613  const unsigned int pattern=~0U, const unsigned int font_height=13,
31614  const bool allow_zero=true) {
31615  if (is_empty()) return *this;
31616  const int yt = (y+3+font_height)<_height?(y+3):(y-2-font_height);
31617  const int siz = (int)values_x.size()-1;
31618  char txt[32] = { 0 };
31619  CImg<T> label;
31620  if (siz<=0) { // Degenerated case.
31621  draw_line(0,y,_width-1,y,color,opacity,pattern);
31622  if (!siz) {
31623  cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_x);
31624  label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
31625  const int
31626  _xt = (width() - label.width())/2,
31627  xt = _xt<3?3:_xt+label.width()>=width()-2?width()-3-label.width():_xt;
31628  draw_point(width()/2,y-1,color,opacity).draw_point(width()/2,y+1,color,opacity);
31629  if (allow_zero || txt[0]!='0' || txt[1]!=0)
31630  draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
31631  }
31632  } else { // Regular case.
31633  if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width-1,y,color,opacity,30,5,pattern);
31634  else draw_arrow(_width-1,y,0,y,color,opacity,30,5,pattern);
31635  cimg_foroff(values_x,x) {
31636  cimg_snprintf(txt,sizeof(txt),"%g",(double)values_x(x));
31637  label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
31638  const int
31639  xi = (int)(x*(_width-1)/siz),
31640  _xt = xi - label.width()/2,
31641  xt = _xt<3?3:_xt+label.width()>=width()-2?width()-3-label.width():_xt;
31642  draw_point(xi,y-1,color,opacity).draw_point(xi,y+1,color,opacity);
31643  if (allow_zero || txt[0]!='0' || txt[1]!=0)
31644  draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
31645  }
31646  }
31647  return *this;
31648  }
31649 
31651 
31660  template<typename t, typename tc>
31661  CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
31662  const tc *const color, const float opacity=1,
31663  const unsigned int pattern=~0U, const unsigned int font_height=13,
31664  const bool allow_zero=true) {
31665  if (is_empty()) return *this;
31666  int siz = (int)values_y.size()-1;
31667  char txt[32] = { 0 };
31668  CImg<T> label;
31669  if (siz<=0) { // Degenerated case.
31670  draw_line(x,0,x,_height-1,color,opacity,pattern);
31671  if (!siz) {
31672  cimg_snprintf(txt,sizeof(txt),"%g",(double)*values_y);
31673  label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
31674  const int
31675  _yt = (height() - label.height())/2,
31676  yt = _yt<0?0:_yt+label.height()>=height()?height()-1-label.height():_yt,
31677  _xt = x - 2 - label.width(),
31678  xt = _xt>=0?_xt:x+3;
31679  draw_point(x-1,height()/2,color,opacity).draw_point(x+1,height()/2,color,opacity);
31680  if (allow_zero || txt[0]!='0' || txt[1]!=0)
31681  draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
31682  }
31683  } else { // Regular case.
31684  if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height-1,color,opacity,30,5,pattern);
31685  else draw_arrow(x,_height-1,x,0,color,opacity,30,5,pattern);
31686  cimg_foroff(values_y,y) {
31687  cimg_snprintf(txt,sizeof(txt),"%g",(double)values_y(y));
31688  label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
31689  const int
31690  yi = (int)(y*(_height-1)/siz),
31691  _yt = yi - label.height()/2,
31692  yt = _yt<0?0:_yt+label.height()>=height()?height()-1-label.height():_yt,
31693  _xt = x - 2 - label.width(),
31694  xt = _xt>=0?_xt:x+3;
31695  draw_point(x-1,yi,color,opacity).draw_point(x+1,yi,color,opacity);
31696  if (allow_zero || txt[0]!='0' || txt[1]!=0)
31697  draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
31698  }
31699  }
31700  return *this;
31701  }
31702 
31704 
31714  template<typename tx, typename ty, typename tc>
31715  CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
31716  const tc *const color, const float opacity=1,
31717  const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
31718  const unsigned int font_height=13, const bool allow_zero=true) {
31719  if (is_empty()) return *this;
31720  const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
31721  const int sizx = (int)values_x.size()-1, wm1 = width()-1;
31722  if (sizx>=0) {
31723  float ox = (float)*nvalues_x;
31724  for (unsigned int x = sizx?1:0; x<_width; ++x) {
31725  const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
31726  if (nx*ox<=0) { draw_axis(nx==0?x:x-1,values_y,color,opacity,pattern_y,font_height,allow_zero); break; }
31727  ox = nx;
31728  }
31729  }
31730  const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
31731  const int sizy = (int)values_y.size()-1, hm1 = height()-1;
31732  if (sizy>0) {
31733  float oy = (float)nvalues_y[0];
31734  for (unsigned int y = sizy?1:0; y<_height; ++y) {
31735  const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
31736  if (ny*oy<=0) { draw_axis(values_x,ny==0?y:y-1,color,opacity,pattern_x,font_height,allow_zero); break; }
31737  oy = ny;
31738  }
31739  }
31740  return *this;
31741  }
31742 
31744  template<typename tc>
31745  CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
31746  const tc *const color, const float opacity=1,
31747  const int subdivisionx=-60, const int subdivisiony=-60,
31748  const float precisionx=0, const float precisiony=0,
31749  const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
31750  const unsigned int font_height=13) {
31751  if (is_empty()) return *this;
31752  const bool allow_zero = (x0*x1>0) || (y0*y1>0);
31753  const float
31754  dx = cimg::abs(x1-x0), dy = cimg::abs(y1-y0),
31755  px = dx<=0?1:precisionx==0?(float)std::pow(10.0,(int)std::log10(dx)-2.0):precisionx,
31756  py = dy<=0?1:precisiony==0?(float)std::pow(10.0,(int)std::log10(dy)-2.0):precisiony;
31757  if (x0!=x1 && y0!=y1)
31758  draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),
31759  CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
31760  color,opacity,pattern_x,pattern_y,font_height,allow_zero);
31761  else if (x0==x1 && y0!=y1)
31762  draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1).round(py),
31763  color,opacity,pattern_y,font_height);
31764  else if (x0!=x1 && y0==y1)
31765  draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1).round(px),(int)y0,
31766  color,opacity,pattern_x,font_height);
31767  return *this;
31768  }
31769 
31771 
31779  template<typename tx, typename ty, typename tc>
31780  CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
31781  const tc *const color, const float opacity=1,
31782  const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
31783  if (is_empty()) return *this;
31784  if (values_x) cimg_foroff(values_x,x) {
31785  const int xi = (int)values_x[x];
31786  if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height-1,color,opacity,pattern_x);
31787  }
31788  if (values_y) cimg_foroff(values_y,y) {
31789  const int yi = (int)values_y[y];
31790  if (yi>=0 && yi<height()) draw_line(0,yi,_width-1,yi,color,opacity,pattern_y);
31791  }
31792  return *this;
31793  }
31794 
31796  template<typename tc>
31797  CImg<T>& draw_grid(const float delta_x, const float delta_y,
31798  const float offsetx, const float offsety,
31799  const bool invertx, const bool inverty,
31800  const tc *const color, const float opacity=1,
31801  const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
31802  if (is_empty()) return *this;
31803  CImg<uintT> seqx, seqy;
31804  if (delta_x!=0) {
31805  const float dx = delta_x>0?delta_x:_width*-delta_x/100;
31806  const unsigned int nx = (unsigned int)(_width/dx);
31807  seqx = CImg<uintT>::sequence(1+nx,0,(unsigned int)(dx*nx));
31808  if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x)+offsetx,(float)_width);
31809  if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
31810  }
31811  if (delta_y!=0) {
31812  const float dy = delta_y>0?delta_y:_height*-delta_y/100;
31813  const unsigned int ny = (unsigned int)(_height/dy);
31814  seqy = CImg<uintT>::sequence(1+ny,0,(unsigned int)(dy*ny));
31815  if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y)+offsety,(float)_height);
31816  if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
31817  }
31818  return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
31819  }
31820 
31822 
31847  template<typename t, typename tc>
31849  const tc *const color, const float opacity=1,
31850  const unsigned int plot_type=1, const int vertex_type=1,
31851  const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
31852  if (is_empty() || _height<=1) return *this;
31853  if (!color)
31854  throw CImgArgumentException(_cimg_instance
31855  "draw_graph(): Specified color is (null).",
31856  cimg_instance);
31857 
31858  // Create shaded colors for displaying bar plots.
31859  CImg<tc> color1, color2;
31860  if (plot_type==3) {
31861  color1.assign(_spectrum); color2.assign(_spectrum);
31862  cimg_forC(*this,c) { color1[c] = (tc)cimg::min((float)cimg::type<tc>::max(),color[c]*1.2f); color2[c] = (tc)(color[c]*0.4f); }
31863  }
31864 
31865  // Compute min/max and normalization factors.
31866  const unsigned long
31867  siz = data.size(),
31868  _siz1 = siz - (plot_type!=3?1:0),
31869  siz1 = _siz1?_siz1:1;
31870  const unsigned int
31871  _width1 = _width - (plot_type!=3?1:0),
31872  width1 = _width1?_width1:1;
31873  double m = ymin, M = ymax;
31874  if (ymin==ymax) m = (double)data.max_min(M);
31875  if (m==M) { --m; ++M; }
31876  const float ca = (float)(M-m)/(_height-1);
31877  bool init_hatch = true;
31878 
31879  // Draw graph edges
31880  switch (plot_type%4) {
31881  case 1 : { // Segments
31882  int oX = 0, oY = (int)((data[0]-m)/ca);
31883  if (siz==1) {
31884  const int Y = (int)((*data-m)/ca);
31885  draw_line(0,Y,width()-1,Y,color,opacity,pattern);
31886  } else for (unsigned long off = 1; off<siz; ++off) {
31887  const int
31888  X = off*_width1/siz1,
31889  Y = (int)((data[off]-m)/ca);
31890  draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
31891  oX = X; oY = Y;
31892  init_hatch = false;
31893  }
31894  } break;
31895  case 2 : { // Spline
31896  const CImg<t> ndata(data._data,siz,1,1,1,true);
31897  int oY = (int)((data[0]-m)/ca);
31898  cimg_forX(*this,x) {
31899  const int Y = (int)((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
31900  if (x>0) draw_line(x,oY,x+1,Y,color,opacity,pattern,init_hatch);
31901  init_hatch = false;
31902  oY = Y;
31903  }
31904  } break;
31905  case 3 : { // Bars
31906  const int Y0 = (int)(-m/ca);
31907  int oX = 0;
31908  cimg_foroff(data,off) {
31909  const int
31910  X = (off+1)*_width/siz-1,
31911  Y = (int)((data[off]-m)/ca);
31912  draw_rectangle(oX,Y0,X,Y,color,opacity).
31913  draw_line(oX,Y,oX,Y0,color2.data(),opacity).
31914  draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
31915  draw_line(X,Y,X,Y0,color1.data(),opacity).
31916  draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
31917  oX = X+1;
31918  }
31919  } break;
31920  default : break; // No edges
31921  }
31922 
31923  // Draw graph points
31924  const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
31925  switch (vertex_type%8) {
31926  case 1 : { // Point
31927  cimg_foroff(data,off) {
31928  const int
31929  X = off*_width1/siz1 + wb2,
31930  Y = (int)((data[off]-m)/ca);
31931  draw_point(X,Y,color,opacity);
31932  }
31933  } break;
31934  case 2 : { // Straight Cross
31935  cimg_foroff(data,off) {
31936  const int
31937  X = off*_width1/siz1 + wb2,
31938  Y = (int)((data[off]-m)/ca);
31939  draw_line(X-3,Y,X+3,Y,color,opacity).draw_line(X,Y-3,X,Y+3,color,opacity);
31940  }
31941  } break;
31942  case 3 : { // Diagonal Cross
31943  cimg_foroff(data,off) {
31944  const int
31945  X = off*_width1/siz1 + wb2,
31946  Y = (int)((data[off]-m)/ca);
31947  draw_line(X-3,Y-3,X+3,Y+3,color,opacity).draw_line(X-3,Y+3,X+3,Y-3,color,opacity);
31948  }
31949  } break;
31950  case 4 : { // Filled Circle
31951  cimg_foroff(data,off) {
31952  const int
31953  X = off*_width1/siz1 + wb2,
31954  Y = (int)((data[off]-m)/ca);
31955  draw_circle(X,Y,3,color,opacity);
31956  }
31957  } break;
31958  case 5 : { // Outlined circle
31959  cimg_foroff(data,off) {
31960  const int
31961  X = off*_width1/siz1 + wb2,
31962  Y = (int)((data[off]-m)/ca);
31963  draw_circle(X,Y,3,color,opacity,0U);
31964  }
31965  } break;
31966  case 6 : { // Square
31967  cimg_foroff(data,off) {
31968  const int
31969  X = off*_width1/siz1 + wb2,
31970  Y = (int)((data[off]-m)/ca);
31971  draw_rectangle(X-3,Y-3,X+3,Y+3,color,opacity,~0U);
31972  }
31973  } break;
31974  case 7 : { // Diamond
31975  cimg_foroff(data,off) {
31976  const int
31977  X = off*_width1/siz1 + wb2,
31978  Y = (int)((data[off]-m)/ca);
31979  draw_line(X,Y-4,X+4,Y,color,opacity).
31980  draw_line(X+4,Y,X,Y+4,color,opacity).
31981  draw_line(X,Y+4,X-4,Y,color,opacity).
31982  draw_line(X-4,Y,X,Y-4,color,opacity);
31983  }
31984  } break;
31985  default : break; // No points
31986  }
31987  return *this;
31988  }
31989 
31991 
32002  template<typename tc, typename t>
32003  CImg<T>& draw_fill(const int x, const int y, const int z,
32004  const tc *const color, const float opacity,
32005  CImg<t>& region, const float sigma=0,
32006  const bool is_high_connexity=false) {
32007 
32008 #define _cimg_draw_fill_test(x,y,z,res) if (region(x,y,z)) res = false; else { \
32009  res = true; \
32010  const T *reference_col = reference_color._data + _spectrum, *ptrs = data(x,y,z) + siz; \
32011  for (unsigned int i = _spectrum; res && i; --i) { ptrs-=whd; res = (cimg::abs(*ptrs - *(--reference_col))<=sigma); } \
32012  region(x,y,z) = (t)(res?1:noregion); \
32013 }
32014 
32015 #define _cimg_draw_fill_set(x,y,z) { \
32016  const tc *col = color; \
32017  T *ptrd = data(x,y,z); \
32018  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; } \
32019  else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; } \
32020 }
32021 
32022 #define _cimg_draw_fill_insert(x,y,z) { \
32023  if (posr1>=remaining._height) remaining.resize(3,remaining._height<<1,1,1,0); \
32024  unsigned int *ptrr = remaining.data(0,posr1); \
32025  *(ptrr++) = x; *(ptrr++) = y; *(ptrr++) = z; ++posr1; \
32026 }
32027 
32028 #define _cimg_draw_fill_test_neighbor(x,y,z,cond) if (cond) { \
32029  const unsigned int tx = x, ty = y, tz = z; \
32030  _cimg_draw_fill_test(tx,ty,tz,res); if (res) _cimg_draw_fill_insert(tx,ty,tz); \
32031 }
32032 
32033  if (!color)
32034  throw CImgArgumentException(_cimg_instance
32035  "draw_fill(): Specified color is (null).",
32036  cimg_instance);
32037 
32038  region.assign(_width,_height,_depth,1,(t)0);
32039  if (x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth()) {
32040  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32041  const unsigned long whd = (unsigned long)_width*_height*_depth, siz = (unsigned long)_spectrum*whd;
32042  const unsigned int W1 = _width-1, H1 = _height-1, D1 = _depth-1;
32043  const bool is_3d = (_depth>1);
32044  const CImg<T> reference_color = get_vector_at(x,y,z);
32045  CImg<uintT> remaining(3,512,1,1,0);
32046  remaining(0,0) = x; remaining(1,0) = y; remaining(2,0) = z;
32047  unsigned int posr0 = 0, posr1 = 1;
32048  region(x,y,z) = (t)1;
32049  const t noregion = ((t)1==(t)2)?(t)0:(t)(-1);
32050  if (is_3d) do { // 3d version of the filling algorithm
32051  const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++), zc = *(pcurr++);
32052  if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
32053  bool cont, res;
32054  unsigned int nxc = xc;
32055  do { // X-backward
32056  _cimg_draw_fill_set(nxc,yc,zc);
32057  _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
32058  _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
32059  _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
32060  _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
32061  if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
32062  } while (cont);
32063  nxc = xc;
32064  do { // X-forward
32065  if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,zc,cont); } else cont = false;
32066  if (cont) {
32067  _cimg_draw_fill_set(nxc,yc,zc);
32068  _cimg_draw_fill_test_neighbor(nxc,yc-1,zc,yc!=0);
32069  _cimg_draw_fill_test_neighbor(nxc,yc+1,zc,yc<H1);
32070  _cimg_draw_fill_test_neighbor(nxc,yc,zc-1,zc!=0);
32071  _cimg_draw_fill_test_neighbor(nxc,yc,zc+1,zc<D1);
32072  }
32073  } while (cont);
32074  unsigned int nyc = yc;
32075  do { // Y-backward
32076  if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
32077  if (cont) {
32078  _cimg_draw_fill_set(xc,nyc,zc);
32079  _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
32080  _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
32081  _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
32082  _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
32083  }
32084  } while (cont);
32085  nyc = yc;
32086  do { // Y-forward
32087  if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,zc,cont); } else cont = false;
32088  if (cont) {
32089  _cimg_draw_fill_set(xc,nyc,zc);
32090  _cimg_draw_fill_test_neighbor(xc-1,nyc,zc,xc!=0);
32091  _cimg_draw_fill_test_neighbor(xc+1,nyc,zc,xc<W1);
32092  _cimg_draw_fill_test_neighbor(xc,nyc,zc-1,zc!=0);
32093  _cimg_draw_fill_test_neighbor(xc,nyc,zc+1,zc<D1);
32094  }
32095  } while (cont);
32096  unsigned int nzc = zc;
32097  do { // Z-backward
32098  if (nzc) { --nzc; _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
32099  if (cont) {
32100  _cimg_draw_fill_set(xc,yc,nzc);
32101  _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
32102  _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
32103  _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
32104  _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
32105  }
32106  } while (cont);
32107  nzc = zc;
32108  do { // Z-forward
32109  if ((++nzc)<=D1) { _cimg_draw_fill_test(xc,yc,nzc,cont); } else cont = false;
32110  if (cont) {
32111  _cimg_draw_fill_set(xc,nyc,zc);
32112  _cimg_draw_fill_test_neighbor(xc-1,yc,nzc,xc!=0);
32113  _cimg_draw_fill_test_neighbor(xc+1,yc,nzc,xc<W1);
32114  _cimg_draw_fill_test_neighbor(xc,yc-1,nzc,yc!=0);
32115  _cimg_draw_fill_test_neighbor(xc,yc+1,nzc,yc<H1);
32116  }
32117  } while (cont);
32118  } while (posr1>posr0);
32119  else do { // 2d version of the filling algorithm
32120  const unsigned int *pcurr = remaining.data(0,posr0++), xc = *(pcurr++), yc = *(pcurr++);
32121  if (posr0>=512) { remaining.shift(0,-(int)posr0); posr1-=posr0; posr0 = 0; }
32122  bool cont, res;
32123  unsigned int nxc = xc;
32124  do { // X-backward
32125  _cimg_draw_fill_set(nxc,yc,0);
32126  _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
32127  _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
32128  if (is_high_connexity) {
32129  _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
32130  _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
32131  _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
32132  _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
32133  }
32134  if (nxc) { --nxc; _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
32135  } while (cont);
32136  nxc = xc;
32137  do { // X-forward
32138  if ((++nxc)<=W1) { _cimg_draw_fill_test(nxc,yc,0,cont); } else cont = false;
32139  if (cont) {
32140  _cimg_draw_fill_set(nxc,yc,0);
32141  _cimg_draw_fill_test_neighbor(nxc,yc-1,0,yc!=0);
32142  _cimg_draw_fill_test_neighbor(nxc,yc+1,0,yc<H1);
32143  if (is_high_connexity) {
32144  _cimg_draw_fill_test_neighbor(nxc-1,yc-1,0,(nxc!=0 && yc!=0));
32145  _cimg_draw_fill_test_neighbor(nxc+1,yc-1,0,(nxc<W1 && yc!=0));
32146  _cimg_draw_fill_test_neighbor(nxc-1,yc+1,0,(nxc!=0 && yc<H1));
32147  _cimg_draw_fill_test_neighbor(nxc+1,yc+1,0,(nxc<W1 && yc<H1));
32148  }
32149  }
32150  } while (cont);
32151  unsigned int nyc = yc;
32152  do { // Y-backward
32153  if (nyc) { --nyc; _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
32154  if (cont) {
32155  _cimg_draw_fill_set(xc,nyc,0);
32156  _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
32157  _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
32158  if (is_high_connexity) {
32159  _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
32160  _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
32161  _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
32162  _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
32163  }
32164  }
32165  } while (cont);
32166  nyc = yc;
32167  do { // Y-forward
32168  if ((++nyc)<=H1) { _cimg_draw_fill_test(xc,nyc,0,cont); } else cont = false;
32169  if (cont) {
32170  _cimg_draw_fill_set(xc,nyc,0);
32171  _cimg_draw_fill_test_neighbor(xc-1,nyc,0,xc!=0);
32172  _cimg_draw_fill_test_neighbor(xc+1,nyc,0,xc<W1);
32173  if (is_high_connexity) {
32174  _cimg_draw_fill_test_neighbor(xc-1,nyc-1,0,(xc!=0 && nyc!=0));
32175  _cimg_draw_fill_test_neighbor(xc+1,nyc-1,0,(xc<W1 && nyc!=0));
32176  _cimg_draw_fill_test_neighbor(xc-1,nyc+1,0,(xc!=0 && nyc<H1));
32177  _cimg_draw_fill_test_neighbor(xc+1,nyc+1,0,(xc<W1 && nyc<H1));
32178  }
32179  }
32180  } while (cont);
32181  } while (posr1>posr0);
32182  if (noregion) cimg_for(region,ptrd,t) if (*ptrd==noregion) *ptrd = (t)0;
32183  }
32184  return *this;
32185  }
32186 
32188  template<typename tc>
32189  CImg<T>& draw_fill(const int x, const int y, const int z,
32190  const tc *const color, const float opacity=1,
32191  const float sigma=0, const bool is_high_connexity=false) {
32192  CImg<boolT> tmp;
32193  return draw_fill(x,y,z,color,opacity,tmp,sigma,is_high_connexity);
32194  }
32195 
32197  template<typename tc>
32198  CImg<T>& draw_fill(const int x, const int y,
32199  const tc *const color, const float opacity=1,
32200  const float sigma=0, const bool is_high_connexity=false) {
32201  CImg<boolT> tmp;
32202  return draw_fill(x,y,0,color,opacity,tmp,sigma,is_high_connexity);
32203  }
32204 
32206 
32212  CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
32213  if (is_empty()) return *this;
32214  const int w = width(), h = height();
32215  const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
32216  cimg_forZC(*this,z,c) {
32217  CImg<T> ref = get_shared_slice(z,c);
32218  for (int d=1<<cimg::min(scale,31U); d>1; d>>=1) {
32219  const int d2 = d>>1;
32220  const float r = alpha*d + beta;
32221  for (int y0=0; y0<h; y0+=d)
32222  for (int x0=0; x0<w; x0+=d) {
32223  const int x1 = (x0 + d)%w, y1 = (y0 + d)%h, xc = (x0 + d2)%w, yc = (y0 + d2)%h;
32224  const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) + r*cimg::crand());
32225  ref(xc,yc) = (T)(val<m?m:val>M?M:val);
32226  }
32227  for (int y=-d2; y<h; y+=d)
32228  for (int x0=0; x0<w; x0+=d) {
32229  const int y0 = cimg::mod(y,h), x1 = (x0 + d)%w, y1 = (y + d)%h, xc = (x0 + d2)%w, yc = (y + d2)%h;
32230  const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + r*cimg::crand());
32231  ref(xc,yc) = (T)(val<m?m:val>M?M:val);
32232  }
32233  for (int y0=0; y0<h; y0+=d)
32234  for (int x=-d2; x<w; x+=d) {
32235  const int x0 = cimg::mod(x,w), x1 = (x + d)%w, y1 = (y0 + d)%h, xc = (x + d2)%w, yc = (y0 + d2)%h;
32236  const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + r*cimg::crand());
32237  ref(xc,yc) = (T)(val<m?m:val>M?M:val);
32238  }
32239  for (int y=-d2; y<h; y+=d)
32240  for (int x=-d2; x<w; x+=d) {
32241  const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + d)%w, y1 = (y + d)%h, xc = (x + d2)%w, yc = (y + d2)%h;
32242  const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) + r*cimg::crand());
32243  ref(xc,yc) = (T)(val<m?m:val>M?M:val);
32244  }
32245  }
32246  }
32247  return *this;
32248  }
32249 
32251 
32269  template<typename tc>
32270  CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
32271  const CImg<tc>& colormap, const float opacity=1,
32272  const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
32273  const unsigned int iteration_max=255,
32274  const bool is_normalized_iteration=false,
32275  const bool is_julia_set=false,
32276  const double param_r=0, const double param_i=0) {
32277  if (is_empty()) return *this;
32278  CImg<tc> palette;
32279  if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
32280  if (palette && palette._spectrum!=_spectrum)
32281  throw CImgArgumentException(_cimg_instance
32282  "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have incompatible dimensions.",
32283  cimg_instance,
32284  colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
32285 
32286  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0), ln2 = (float)std::log(2.0);
32287  unsigned int iteration = 0;
32288  cimg_for_inXY(*this,x0,y0,x1,y1,p,q) {
32289  const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
32290  double zr, zi, cr, ci;
32291  if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
32292  else { zr = param_r; zi = param_i; cr = x; ci = y; }
32293  for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
32294  const double temp = zr*zr - zi*zi + cr;
32295  zi = 2*zr*zi + ci;
32296  zr = temp;
32297  }
32298  if (iteration>iteration_max) {
32299  if (palette) {
32300  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
32301  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
32302  } else {
32303  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
32304  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
32305  }
32306  } else if (is_normalized_iteration) {
32307  const float
32308  normz = (float)cimg::abs(zr*zr+zi*zi),
32309  niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
32310  if (palette) {
32311  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
32312  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
32313  } else {
32314  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
32315  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
32316  }
32317  } else {
32318  if (palette) {
32319  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
32320  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
32321  } else {
32322  if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
32323  else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
32324  }
32325  }
32326  }
32327  return *this;
32328  }
32329 
32331  template<typename tc>
32332  CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
32333  const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
32334  const unsigned int iteration_max=255,
32335  const bool is_normalized_iteration=false,
32336  const bool is_julia_set=false,
32337  const double param_r=0, const double param_i=0) {
32338  return draw_mandelbrot(0,0,_width-1,_height-1,colormap,opacity,
32339  z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
32340  }
32341 
32343 
32349  template<typename tc>
32350  CImg<T>& draw_gaussian(const float xc, const float sigma,
32351  const tc *const color, const float opacity=1) {
32352  if (is_empty()) return *this;
32353  if (!color)
32354  throw CImgArgumentException(_cimg_instance
32355  "draw_gaussian(): Specified color is (null).",
32356  cimg_instance);
32357  const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32358  const unsigned long whd = (unsigned long)_width*_height*_depth;
32359  const tc *col = color;
32360  cimg_forX(*this,x) {
32361  const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
32362  T *ptrd = data(x,0,0,0);
32363  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
32364  else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
32365  col-=_spectrum;
32366  }
32367  return *this;
32368  }
32369 
32371 
32378  template<typename t, typename tc>
32379  CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
32380  const tc *const color, const float opacity=1) {
32381  if (is_empty()) return *this;
32382  if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
32383  throw CImgArgumentException(_cimg_instance
32384  "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
32385  cimg_instance,
32386  tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
32387  if (!color)
32388  throw CImgArgumentException(_cimg_instance
32389  "draw_gaussian(): Specified color is (null).",
32390  cimg_instance);
32391  typedef typename CImg<t>::Tfloat tfloat;
32392  const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
32393  const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
32394  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32395  const unsigned long whd = (unsigned long)_width*_height*_depth;
32396  const tc *col = color;
32397  float dy = -yc;
32398  cimg_forY(*this,y) {
32399  float dx = -xc;
32400  cimg_forX(*this,x) {
32401  const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
32402  T *ptrd = data(x,y,0,0);
32403  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
32404  else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
32405  col-=_spectrum;
32406  ++dx;
32407  }
32408  ++dy;
32409  }
32410  return *this;
32411  }
32412 
32414  template<typename tc>
32415  CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
32416  const tc *const color, const float opacity=1) {
32417  const double
32418  a = r1*ru*ru + r2*rv*rv,
32419  b = (r1-r2)*ru*rv,
32420  c = r1*rv*rv + r2*ru*ru;
32421  const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
32422  return draw_gaussian(xc,yc,tensor,color,opacity);
32423  }
32424 
32426  template<typename tc>
32427  CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
32428  const tc *const color, const float opacity=1) {
32429  return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
32430  }
32431 
32433  template<typename t, typename tc>
32434  CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
32435  const tc *const color, const float opacity=1) {
32436  if (is_empty()) return *this;
32437  typedef typename CImg<t>::Tfloat tfloat;
32438  if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
32439  throw CImgArgumentException(_cimg_instance
32440  "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
32441  cimg_instance,
32442  tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
32443 
32444  const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/(-2.0);
32445  const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = 2*invT2(2,0), d = invT2(1,1), e = 2*invT2(2,1), f = invT2(2,2);
32446  const float nopacity = cimg::abs(opacity), copacity = 1 - cimg::max(opacity,0);
32447  const unsigned long whd = (unsigned long)_width*_height*_depth;
32448  const tc *col = color;
32449  cimg_forXYZ(*this,x,y,z) {
32450  const float
32451  dx = (x - xc), dy = (y - yc), dz = (z - zc),
32452  val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
32453  T *ptrd = data(x,y,z,0);
32454  if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
32455  else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
32456  col-=_spectrum;
32457  }
32458  return *this;
32459  }
32460 
32462  template<typename tc>
32463  CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
32464  const tc *const color, const float opacity=1) {
32465  return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
32466  }
32467 
32469 
32486  template<typename tp, typename tf, typename tc, typename to>
32487  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32488  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32489  const CImgList<tc>& colors, const CImg<to>& opacities,
32490  const unsigned int render_type=4,
32491  const bool is_double_sided=false, const float focale=500,
32492  const float lightx=0, const float lighty=0, const float lightz=-5e8,
32493  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
32494  return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz,
32495  specular_lightness,specular_shininess,CImg<floatT>::empty());
32496  }
32497 
32499  template<typename tp, typename tf, typename tc, typename to, typename tz>
32500  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32501  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32502  const CImgList<tc>& colors, const CImg<to>& opacities,
32503  const unsigned int render_type,
32504  const bool is_double_sided, const float focale,
32505  const float lightx, const float lighty, const float lightz,
32506  const float specular_lightness, const float specular_shininess,
32507  CImg<tz>& zbuffer) {
32508  return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
32509  render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,1);
32510  }
32511 
32512 #ifdef cimg_use_board
32513  template<typename tp, typename tf, typename tc, typename to>
32514  CImg<T>& draw_object3d(LibBoard::Board& board,
32515  const float x0, const float y0, const float z0,
32516  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32517  const CImgList<tc>& colors, const CImg<to>& opacities,
32518  const unsigned int render_type=4,
32519  const bool is_double_sided=false, const float focale=500,
32520  const float lightx=0, const float lighty=0, const float lightz=-5e8,
32521  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
32522  return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz,
32523  specular_lightness,specular_shininess,CImg<floatT>::empty());
32524  }
32525 
32526  template<typename tp, typename tf, typename tc, typename to, typename tz>
32527  CImg<T>& draw_object3d(LibBoard::Board& board,
32528  const float x0, const float y0, const float z0,
32529  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32530  const CImgList<tc>& colors, const CImg<to>& opacities,
32531  const unsigned int render_type,
32532  const bool is_double_sided, const float focale,
32533  const float lightx, const float lighty, const float lightz,
32534  const float specular_lightness, const float specular_shininess,
32535  CImg<tz>& zbuffer) {
32536  return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
32537  render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,1);
32538  }
32539 #endif
32540 
32542  template<typename tp, typename tf, typename tc, typename to>
32543  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32544  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32545  const CImgList<tc>& colors, const CImgList<to>& opacities,
32546  const unsigned int render_type=4,
32547  const bool is_double_sided=false, const float focale=500,
32548  const float lightx=0, const float lighty=0, const float lightz=-5e8,
32549  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
32550  return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz,
32551  specular_lightness,specular_shininess,CImg<floatT>::empty());
32552  }
32553 
32555  template<typename tp, typename tf, typename tc, typename to, typename tz>
32556  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32557  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32558  const CImgList<tc>& colors, const CImgList<to>& opacities,
32559  const unsigned int render_type,
32560  const bool is_double_sided, const float focale,
32561  const float lightx, const float lighty, const float lightz,
32562  const float specular_lightness, const float specular_shininess,
32563  CImg<tz>& zbuffer) {
32564  return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
32565  render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,1);
32566  }
32567 
32568 #ifdef cimg_use_board
32569  template<typename tp, typename tf, typename tc, typename to>
32570  CImg<T>& draw_object3d(LibBoard::Board& board,
32571  const float x0, const float y0, const float z0,
32572  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32573  const CImgList<tc>& colors, const CImgList<to>& opacities,
32574  const unsigned int render_type=4,
32575  const bool is_double_sided=false, const float focale=500,
32576  const float lightx=0, const float lighty=0, const float lightz=-5e8,
32577  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
32578  return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,is_double_sided,focale,lightx,lighty,lightz,
32579  specular_lightness,specular_shininess,CImg<floatT>::empty());
32580  }
32581 
32582  template<typename tp, typename tf, typename tc, typename to, typename tz>
32583  CImg<T>& draw_object3d(LibBoard::Board& board,
32584  const float x0, const float y0, const float z0,
32585  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32586  const CImgList<tc>& colors, const CImgList<to>& opacities,
32587  const unsigned int render_type,
32588  const bool is_double_sided, const float focale,
32589  const float lightx, const float lighty, const float lightz,
32590  const float specular_lightness, const float specular_shininess,
32591  CImg<tz>& zbuffer) {
32592  return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
32593  render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,1);
32594  }
32595 #endif
32596 
32598  template<typename tp, typename tf, typename tc>
32599  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32600  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32601  const CImgList<tc>& colors,
32602  const unsigned int render_type=4,
32603  const bool is_double_sided=false, const float focale=500,
32604  const float lightx=0, const float lighty=0, const float lightz=-5e8,
32605  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
32606  return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
32607  render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,CImg<floatT>::empty());
32608  }
32609 
32611  template<typename tp, typename tf, typename tc, typename tz>
32612  CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
32613  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32614  const CImgList<tc>& colors,
32615  const unsigned int render_type,
32616  const bool is_double_sided, const float focale,
32617  const float lightx, const float lighty, const float lightz,
32618  const float specular_lightness, const float specular_shininess,
32619  CImg<tz>& zbuffer) {
32620  return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
32621  render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,zbuffer);
32622  }
32623 
32624 #ifdef cimg_use_board
32625  template<typename tp, typename tf, typename tc, typename to>
32626  CImg<T>& draw_object3d(LibBoard::Board& board,
32627  const float x0, const float y0, const float z0,
32628  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32629  const CImgList<tc>& colors,
32630  const unsigned int render_type=4,
32631  const bool is_double_sided=false, const float focale=500,
32632  const float lightx=0, const float lighty=0, const float lightz=-5e8,
32633  const float specular_lightness=0.2f, const float specular_shininess=0.1f) {
32634  return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
32635  render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,CImg<floatT>::empty());
32636  }
32637 
32638  template<typename tp, typename tf, typename tc, typename to, typename tz>
32639  CImg<T>& draw_object3d(LibBoard::Board& board,
32640  const float x0, const float y0, const float z0,
32641  const CImg<tp>& vertices, const CImgList<tf>& primitives,
32642  const CImgList<tc>& colors,
32643  const unsigned int render_type,
32644  const bool is_double_sided, const float focale,
32645  const float lightx, const float lighty, const float lightz,
32646  const float specular_lightness, const float specular_shininess,
32647  CImg<tz>& zbuffer) {
32648  return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::empty(),
32649  render_type,is_double_sided,focale,lightx,lighty,lightz,specular_lightness,specular_shininess,zbuffer);
32650  }
32651 #endif
32652 
32653  template<typename t>
32654  unsigned int ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) const {
32655  return (n_primitive>=opacities._width || opacities[n_primitive].is_empty())?1:opacities[n_primitive].size();
32656  }
32657 
32658  template<typename t>
32659  unsigned int ___draw_object3d(const CImg<t>&, const unsigned int) const {
32660  return 1;
32661  }
32662 
32663  template<typename tc, typename to>
32664  void __draw_object3d(const unsigned int n_primitive, const CImgList<to>& opacities, const CImg<tc>& color,
32665  const int nx0, const int ny0, const CImg<T>& sprite, const float opac, const float factor) {
32666  if (n_primitive<opacities._width && opacities[n_primitive]) {
32667  const CImg<to>& opacity = opacities[n_primitive];
32668  if (opacity.size()==1) draw_image(nx0,ny0,sprite,opac);
32669  else if (opacity.is_sameXY(sprite)) draw_image(nx0,ny0,sprite,opacity);
32670  else if (opacity.is_sameXY(color)) draw_image(nx0,ny0,sprite,opacity.get_resize(sprite._width,sprite._height));
32671  else {
32672  const unsigned int
32673  W = cimg::max(sprite._width,(unsigned int)(opacity._width*factor)),
32674  H = cimg::max(sprite._height,(unsigned int)(opacity._height*factor));
32675  const CImg<T> __sprite = (sprite._width==W && sprite._height==H)?CImg<T>():sprite.get_resize(W,H), &_sprite = __sprite?__sprite:sprite;
32676  const CImg<to> __opacity = (opacity._width==W && opacity._height==H)?CImg<to>():opacity.get_resize(W,H), &_opacity = __opacity?__opacity:opacity;
32677  draw_image(nx0,ny0,_sprite,_opacity);
32678  }
32679  } else draw_image(nx0,ny0,sprite,opac);
32680  }
32681 
32682  template<typename tc, typename to>
32683  void __draw_object3d(const unsigned int, const CImg<to>&, const CImg<tc>&,
32684  const int nx0, const int ny0, const CImg<T>& sprite, const float opac, const float) {
32685  draw_image(nx0,ny0,sprite,opac);
32686  }
32687 
32688  template<typename tz, typename tp, typename tf, typename tc, typename to>
32689  CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
32690  const float X, const float Y, const float Z,
32691  const CImg<tp>& vertices,
32692  const CImgList<tf>& primitives,
32693  const CImgList<tc>& colors,
32694  const to& opacities,
32695  const unsigned int render_type,
32696  const bool is_double_sided, const float focale,
32697  const float lightx, const float lighty, const float lightz,
32698  const float specular_lightness, const float specular_shininess,
32699  const float sprite_scale) {
32700  typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
32701  if (is_empty() || !vertices || !primitives) return *this;
32702  char error_message[1024] = { 0 };
32703  if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
32704  throw CImgArgumentException(_cimg_instance
32705  "draw_object3d(): Invalid specified 3d object (%u,%u) (%s).",
32706  cimg_instance,vertices._width,primitives._width,error_message);
32707 #ifndef cimg_use_board
32708  if (pboard) return *this;
32709 #endif
32710  const float
32711  nspec = 1 - (specular_lightness<0.0f?0.0f:(specular_lightness>1.0f?1.0f:specular_lightness)),
32712  nspec2 = 1 + (specular_shininess<0.0f?0.0f:specular_shininess),
32713  nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
32714  nsl2 = 1 - 2*nsl1*nspec,
32715  nsl3 = nspec2 - nsl1 - nsl2;
32716 
32717  // Create light texture for phong-like rendering.
32718  CImg<floatT> light_texture;
32719  if (render_type==5) {
32720  if (colors._width>primitives._width) {
32721  static CImg<floatT> default_light_texture;
32722  static const tc *lptr = 0;
32723  static tc ref_values[64] = { 0 };
32724  const CImg<tc>& img = colors.back();
32725  bool is_same_texture = (lptr==img._data);
32726  if (is_same_texture)
32727  for (unsigned int r = 0, j = 0; j<8; ++j)
32728  for (unsigned int i = 0; i<8; ++i)
32729  if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum)) { is_same_texture = false; break; }
32730  if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
32731  (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
32732  lptr = colors.back().data();
32733  for (unsigned int r = 0, j = 0; j<8; ++j)
32734  for (unsigned int i = 0; i<8; ++i)
32735  ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i+j)%img._spectrum);
32736  }
32737  light_texture.assign(default_light_texture,true);
32738  } else {
32739  static CImg<floatT> default_light_texture;
32740  static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0;
32741  if (!default_light_texture ||
32742  lightx!=olightx || lighty!=olighty || lightz!=olightz ||
32743  specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) {
32744  default_light_texture.assign(512,512);
32745  const float
32746  dlx = lightx - X,
32747  dly = lighty - Y,
32748  dlz = lightz - Z,
32749  nl = (float)std::sqrt(dlx*dlx + dly*dly + dlz*dlz),
32750  nlx = default_light_texture._width/2*(1 + dlx/nl),
32751  nly = default_light_texture._height/2*(1 + dly/nl),
32752  white[] = { 1 };
32753  default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.0f,white);
32754  cimg_forXY(default_light_texture,x,y) {
32755  const float factor = default_light_texture(x,y);
32756  if (factor>nspec) default_light_texture(x,y) = cimg::min(2,nsl1*factor*factor + nsl2*factor + nsl3);
32757  }
32758  default_light_texture.resize(-100,-100,1,_spectrum);
32759  olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess;
32760  }
32761  light_texture.assign(default_light_texture,true);
32762  }
32763  }
32764 
32765  // Compute 3d to 2d projection.
32766  CImg<tpfloat> projections(vertices._width,2);
32767  tpfloat parallzmin = cimg::type<tpfloat>::max();
32768  const float
32769  absfocale = focale?cimg::abs(focale):0,
32770  _focale = absfocale?absfocale:(1-parallzmin);
32771  if (absfocale) cimg_forX(projections,l) { // Perspective projection
32772  const tpfloat
32773  x = (tpfloat)vertices(l,0),
32774  y = (tpfloat)vertices(l,1),
32775  z = (tpfloat)vertices(l,2);
32776  const tpfloat projectedz = z + Z + absfocale;
32777  projections(l,1) = Y + absfocale*y/projectedz;
32778  projections(l,0) = X + absfocale*x/projectedz;
32779  } else cimg_forX(projections,l) { // Parallel projection
32780  const tpfloat
32781  x = (tpfloat)vertices(l,0),
32782  y = (tpfloat)vertices(l,1),
32783  z = (tpfloat)vertices(l,2);
32784  if (z<parallzmin) parallzmin = z;
32785  projections(l,1) = Y + y;
32786  projections(l,0) = X + x;
32787  }
32788 
32789  // Compute and sort visible primitives.
32790  CImg<uintT> visibles(primitives._width);
32791  CImg<tpfloat> zrange(primitives._width);
32792  unsigned int nb_visibles = 0;
32793  const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
32794  cimglist_for(primitives,l) {
32795  const CImg<tf>& primitive = primitives[l];
32796  switch (primitive.size()) {
32797  case 1 : { // Point
32798  const unsigned int i0 = (unsigned int)primitive(0);
32799  const tpfloat z0 = Z + vertices(i0,2);
32800  if (z0>zmin) {
32801  visibles(nb_visibles) = (unsigned int)l;
32802  zrange(nb_visibles++) = z0;
32803  }
32804  } break;
32805  case 5 : { // Sphere
32806  const unsigned int
32807  i0 = (unsigned int)primitive(0),
32808  i1 = (unsigned int)primitive(1);
32809  const tpfloat
32810  Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
32811  Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
32812  Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
32813  _zc = Z + Zc,
32814  zc = _zc + _focale,
32815  xc = X + Xc*(absfocale?absfocale/zc:1),
32816  yc = Y + Yc*(absfocale?absfocale/zc:1),
32817  radius = 0.5f*std::sqrt(cimg::sqr(vertices(i1,0) - vertices(i0,0)) +
32818  cimg::sqr(vertices(i1,1) - vertices(i0,1)) +
32819  cimg::sqr(vertices(i1,2) - vertices(i0,2)))*(absfocale?absfocale/zc:1),
32820  xm = xc - radius,
32821  ym = yc - radius,
32822  xM = xc + radius,
32823  yM = yc + radius;
32824  if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
32825  visibles(nb_visibles) = (unsigned int)l;
32826  zrange(nb_visibles++) = _zc;
32827  }
32828  } break;
32829  case 2 : // Segment
32830  case 6 : {
32831  const unsigned int
32832  i0 = (unsigned int)primitive(0),
32833  i1 = (unsigned int)primitive(1);
32834  const tpfloat
32835  x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
32836  x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
32837  tpfloat xm, xM, ym, yM;
32838  if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
32839  if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
32840  if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
32841  visibles(nb_visibles) = (unsigned int)l;
32842  zrange(nb_visibles++) = (z0 + z1)/2;
32843  }
32844  } break;
32845  case 3 : // Triangle
32846  case 9 : {
32847  const unsigned int
32848  i0 = (unsigned int)primitive(0),
32849  i1 = (unsigned int)primitive(1),
32850  i2 = (unsigned int)primitive(2);
32851  const tpfloat
32852  x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
32853  x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
32854  x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
32855  tpfloat xm, xM, ym, yM;
32856  if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
32857  if (x2<xm) xm = x2;
32858  if (x2>xM) xM = x2;
32859  if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
32860  if (y2<ym) ym = y2;
32861  if (y2>yM) yM = y2;
32862  if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
32863  const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
32864  if (is_double_sided || d<0) {
32865  visibles(nb_visibles) = (unsigned int)l;
32866  zrange(nb_visibles++) = (z0 + z1 + z2)/3;
32867  }
32868  }
32869  } break;
32870  case 4 : // Rectangle
32871  case 12 : {
32872  const unsigned int
32873  i0 = (unsigned int)primitive(0),
32874  i1 = (unsigned int)primitive(1),
32875  i2 = (unsigned int)primitive(2),
32876  i3 = (unsigned int)primitive(3);
32877  const tpfloat
32878  x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
32879  x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
32880  x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
32881  x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
32882  tpfloat xm, xM, ym, yM;
32883  if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
32884  if (x2<xm) xm = x2;
32885  if (x2>xM) xM = x2;
32886  if (x3<xm) xm = x3;
32887  if (x3>xM) xM = x3;
32888  if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
32889  if (y2<ym) ym = y2;
32890  if (y2>yM) yM = y2;
32891  if (y3<ym) ym = y3;
32892  if (y3>yM) yM = y3;
32893  if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
32894  const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
32895  if (is_double_sided || d<0) {
32896  visibles(nb_visibles) = (unsigned int)l;
32897  zrange(nb_visibles++) = (z0 + z1 + z2 + z3)/4;
32898  }
32899  }
32900  } break;
32901  default :
32902  throw CImgArgumentException(_cimg_instance
32903  "draw_object3d(): Invalid primitive[%u] with size %u "
32904  "(should have size 1,2,3,4,5,6,9 or 12).",
32905  cimg_instance,
32906  l,primitive.size());
32907  }
32908  }
32909  if (nb_visibles<=0) return *this;
32910  CImg<uintT> permutations;
32911  CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,false);
32912 
32913  // Compute light properties
32914  CImg<floatT> lightprops;
32915  switch (render_type) {
32916  case 3 : { // Flat Shading
32917  lightprops.assign(nb_visibles);
32918  cimg_forX(lightprops,l) {
32919  const CImg<tf>& primitive = primitives(visibles(permutations(l)));
32920  const unsigned int psize = primitive.size();
32921  if (psize==3 || psize==4 || psize==9 || psize==12) {
32922  const unsigned int
32923  i0 = (unsigned int)primitive(0),
32924  i1 = (unsigned int)primitive(1),
32925  i2 = (unsigned int)primitive(2);
32926  const tpfloat
32927  x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
32928  x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
32929  x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
32930  dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
32931  dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
32932  nx = dy1*dz2 - dz1*dy2,
32933  ny = dz1*dx2 - dx1*dz2,
32934  nz = dx1*dy2 - dy1*dx2,
32935  norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
32936  lx = X + (x0 + x1 + x2)/3 - lightx,
32937  ly = Y + (y0 + y1 + y2)/3 - lighty,
32938  lz = Z + (z0 + z1 + z2)/3 - lightz,
32939  nl = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
32940  factor = cimg::max(cimg::abs(-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
32941  lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
32942  } else lightprops[l] = 1;
32943  }
32944  } break;
32945 
32946  case 4 : // Gouraud Shading
32947  case 5 : { // Phong-Shading
32948  CImg<tpfloat> vertices_normals(vertices._width,3,1,1,0);
32949  for (unsigned int l = 0; l<nb_visibles; ++l) {
32950  const CImg<tf>& primitive = primitives[visibles(l)];
32951  const unsigned int psize = primitive.size();
32952  const bool
32953  triangle_flag = (psize==3) || (psize==9),
32954  rectangle_flag = (psize==4) || (psize==12);
32955  if (triangle_flag || rectangle_flag) {
32956  const unsigned int
32957  i0 = (unsigned int)primitive(0),
32958  i1 = (unsigned int)primitive(1),
32959  i2 = (unsigned int)primitive(2),
32960  i3 = rectangle_flag?(unsigned int)primitive(3):0;
32961  const tpfloat
32962  x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
32963  x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
32964  x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
32965  dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
32966  dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
32967  nnx = dy1*dz2 - dz1*dy2,
32968  nny = dz1*dx2 - dx1*dz2,
32969  nnz = dx1*dy2 - dy1*dx2,
32970  norm = (tpfloat)(1e-5f + std::sqrt(nnx*nnx + nny*nny + nnz*nnz)),
32971  nx = nnx/norm,
32972  ny = nny/norm,
32973  nz = nnz/norm;
32974  vertices_normals(i0,0)+=nx; vertices_normals(i0,1)+=ny; vertices_normals(i0,2)+=nz;
32975  vertices_normals(i1,0)+=nx; vertices_normals(i1,1)+=ny; vertices_normals(i1,2)+=nz;
32976  vertices_normals(i2,0)+=nx; vertices_normals(i2,1)+=ny; vertices_normals(i2,2)+=nz;
32977  if (rectangle_flag) { vertices_normals(i3,0)+=nx; vertices_normals(i3,1)+=ny; vertices_normals(i3,2)+=nz; }
32978  }
32979  }
32980 
32981  if (is_double_sided) cimg_forX(vertices_normals,p) if (vertices_normals(p,2)>0) {
32982  vertices_normals(p,0) = -vertices_normals(p,0);
32983  vertices_normals(p,1) = -vertices_normals(p,1);
32984  vertices_normals(p,2) = -vertices_normals(p,2);
32985  }
32986 
32987  if (render_type==4) {
32988  lightprops.assign(vertices._width);
32989  cimg_forX(lightprops,l) {
32990  const tpfloat
32991  nx = vertices_normals(l,0),
32992  ny = vertices_normals(l,1),
32993  nz = vertices_normals(l,2),
32994  norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
32995  lx = X + vertices(l,0) - lightx,
32996  ly = Y + vertices(l,1) - lighty,
32997  lz = Z + vertices(l,2) - lightz,
32998  nl = (tpfloat)std::sqrt(1e-5f + lx*lx + ly*ly + lz*lz),
32999  factor = cimg::max((-lx*nx-ly*ny-lz*nz)/(norm*nl),0);
33000  lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
33001  }
33002  } else {
33003  const unsigned int
33004  lw2 = light_texture._width/2 - 1,
33005  lh2 = light_texture._height/2 - 1;
33006  lightprops.assign(vertices._width,2);
33007  cimg_forX(lightprops,l) {
33008  const tpfloat
33009  nx = vertices_normals(l,0),
33010  ny = vertices_normals(l,1),
33011  nz = vertices_normals(l,2),
33012  norm = (tpfloat)std::sqrt(1e-5f + nx*nx + ny*ny + nz*nz),
33013  nnx = nx/norm,
33014  nny = ny/norm;
33015  lightprops(l,0) = lw2*(1 + nnx);
33016  lightprops(l,1) = lh2*(1 + nny);
33017  }
33018  }
33019  } break;
33020  }
33021 
33022  // Draw visible primitives
33023  const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
33024  for (unsigned int l = 0; l<nb_visibles; ++l) {
33025  const unsigned int n_primitive = visibles(permutations(l));
33026  const CImg<tf>& primitive = primitives[n_primitive];
33027  const CImg<tc>
33028  &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
33029  _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?colors[n_primitive].get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
33030  &color = _color?_color:(__color?__color:default_color);
33031  const tc *const pcolor = color._data;
33032  const unsigned int siz_opac = ___draw_object3d(opacities,n_primitive);
33033  const float opac = (n_primitive>=opacities._width || !siz_opac)?1.0f:opacities(n_primitive,0);
33034 
33035 #ifdef cimg_use_board
33036  LibBoard::Board &board = *(LibBoard::Board*)pboard;
33037 #endif
33038 
33039  switch (primitive.size()) {
33040  case 1 : { // Colored point or sprite
33041  const unsigned int n0 = (unsigned int)primitive[0];
33042  const int x0 = (int)projections(n0,0), y0 = (int)projections(n0,1);
33043  if (color.size()==_spectrum && siz_opac==1) { // Colored point.
33044  draw_point(x0,y0,pcolor,opac);
33045 #ifdef cimg_use_board
33046  if (pboard) {
33047  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33048  board.fillCircle((float)x0,height()-(float)y0,0);
33049  }
33050 #endif
33051  } else { // Colored sprite.
33052  if (!__color)
33053  throw CImgArgumentException(_cimg_instance
33054  "draw_object3d(): Undefined texture for sprite primitive [%u].",
33055  cimg_instance,n_primitive);
33056  const tpfloat z = Z + vertices(n0,2);
33057  const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
33058  const unsigned int
33059  _sw = (unsigned int)(color._width*factor),
33060  _sh = (unsigned int)(color._height*factor),
33061  sw = _sw?_sw:1, sh = _sh?_sh:1;
33062  const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
33063  if (sw<=3*_width/2 && sh<=3*_height/2 && (nx0+(int)sw/2>=0 || nx0-(int)sw/2<width() || ny0+(int)sh/2>=0 || ny0-(int)sh/2<height())) {
33064  const CImg<tc>
33065  _sprite = (sw!=color._width || sh!=color._height)?color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
33066  &sprite = _sprite?_sprite:color;
33067  __draw_object3d(n_primitive,opacities,color,nx0,ny0,sprite,opac,factor);
33068 #ifdef cimg_use_board
33069  if (pboard) {
33070  board.setPenColorRGBi(128,128,128);
33071  board.setFillColor(LibBoard::Color::None);
33072  board.drawRectangle((float)nx0,height()-(float)ny0,sw,sh);
33073  }
33074 #endif
33075  }
33076  }
33077  } break;
33078  case 2 : { // Colored line
33079  const unsigned int
33080  n0 = (unsigned int)primitive[0],
33081  n1 = (unsigned int)primitive[1];
33082  const int
33083  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33084  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
33085  const float
33086  z0 = vertices(n0,2) + Z + _focale,
33087  z1 = vertices(n1,2) + Z + _focale;
33088  if (render_type) {
33089  if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac);
33090  else draw_line(x0,y0,x1,y1,pcolor,opac);
33091 #ifdef cimg_use_board
33092  if (pboard) {
33093  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33094  board.drawLine((float)x0,height()-(float)y0,x1,height()-(float)y1);
33095  }
33096 #endif
33097  } else {
33098  draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac);
33099 #ifdef cimg_use_board
33100  if (pboard) {
33101  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33102  board.drawCircle((float)x0,height()-(float)y0,0);
33103  board.drawCircle((float)x1,height()-(float)y1,0);
33104  }
33105 #endif
33106  }
33107  } break;
33108  case 5 : { // Colored sphere
33109  const unsigned int
33110  n0 = (unsigned int)primitive[0],
33111  n1 = (unsigned int)primitive[1];
33112  const float
33113  Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0)),
33114  Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1)),
33115  Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2)),
33116  zc = Z + Zc + _focale,
33117  xc = X + Xc*(absfocale?absfocale/zc:1),
33118  yc = Y + Yc*(absfocale?absfocale/zc:1),
33119  radius = 0.5f*std::sqrt(cimg::sqr(vertices(n1,0) - vertices(n0,0)) +
33120  cimg::sqr(vertices(n1,1) - vertices(n0,1)) +
33121  cimg::sqr(vertices(n1,2) - vertices(n0,2)))*(absfocale?absfocale/zc:1);
33122  switch (render_type) {
33123  case 0 :
33124  draw_point((int)xc,(int)yc,pcolor,opac);
33125 #ifdef cimg_use_board
33126  if (pboard) {
33127  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33128  board.fillCircle(xc,height()-yc,0);
33129  }
33130 #endif
33131  break;
33132  case 1 :
33133  draw_circle((int)xc,(int)yc,(int)radius,pcolor,opac,~0U);
33134 #ifdef cimg_use_board
33135  if (pboard) {
33136  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33137  board.setFillColor(LibBoard::Color::None);
33138  board.drawCircle(xc,height()-yc,radius);
33139  }
33140 #endif
33141  break;
33142  default :
33143  draw_circle((int)xc,(int)yc,(int)radius,pcolor,opac);
33144 #ifdef cimg_use_board
33145  if (pboard) {
33146  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33147  board.fillCircle(xc,height()-yc,radius);
33148  }
33149 #endif
33150  break;
33151  }
33152  } break;
33153  case 6 : { // Textured line
33154  if (!__color)
33155  throw CImgArgumentException(_cimg_instance
33156  "draw_object3d(): Undefined texture for line primitive [%u].",
33157  cimg_instance,n_primitive);
33158  const unsigned int
33159  n0 = (unsigned int)primitive[0],
33160  n1 = (unsigned int)primitive[1],
33161  tx0 = (unsigned int)primitive[2],
33162  ty0 = (unsigned int)primitive[3],
33163  tx1 = (unsigned int)primitive[4],
33164  ty1 = (unsigned int)primitive[5];
33165  const int
33166  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33167  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1);
33168  const float
33169  z0 = vertices(n0,2) + Z + _focale,
33170  z1 = vertices(n1,2) + Z + _focale;
33171  if (render_type) {
33172  if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac);
33173  else draw_line(x0,y0,x1,y1,color,tx0,ty0,tx1,ty1,opac);
33174 #ifdef cimg_use_board
33175  if (pboard) {
33176  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33177  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33178  }
33179 #endif
33180  } else {
33181  draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
33182  draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac);
33183 #ifdef cimg_use_board
33184  if (pboard) {
33185  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33186  board.drawCircle((float)x0,height()-(float)y0,0);
33187  board.drawCircle((float)x1,height()-(float)y1,0);
33188  }
33189 #endif
33190  }
33191  } break;
33192  case 3 : { // Colored triangle
33193  const unsigned int
33194  n0 = (unsigned int)primitive[0],
33195  n1 = (unsigned int)primitive[1],
33196  n2 = (unsigned int)primitive[2];
33197  const int
33198  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33199  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
33200  x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
33201  const float
33202  z0 = vertices(n0,2) + Z + _focale,
33203  z1 = vertices(n1,2) + Z + _focale,
33204  z2 = vertices(n2,2) + Z + _focale;
33205  switch (render_type) {
33206  case 0 :
33207  draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac).draw_point(x2,y2,pcolor,opac);
33208 #ifdef cimg_use_board
33209  if (pboard) {
33210  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33211  board.drawCircle((float)x0,height()-(float)y0,0);
33212  board.drawCircle((float)x1,height()-(float)y1,0);
33213  board.drawCircle((float)x2,height()-(float)y2,0);
33214  }
33215 #endif
33216  break;
33217  case 1 :
33218  if (zbuffer)
33219  draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opac).
33220  draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac);
33221  else
33222  draw_line(x0,y0,x1,y1,pcolor,opac).draw_line(x0,y0,x2,y2,pcolor,opac).
33223  draw_line(x1,y1,x2,y2,pcolor,opac);
33224 #ifdef cimg_use_board
33225  if (pboard) {
33226  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33227  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33228  board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
33229  board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33230  }
33231 #endif
33232  break;
33233  case 2 :
33234  if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac);
33235  else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac);
33236 #ifdef cimg_use_board
33237  if (pboard) {
33238  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33239  board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33240  }
33241 #endif
33242  break;
33243  case 3 :
33244  if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac,lightprops(l));
33245  else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac,lightprops(l));
33246 #ifdef cimg_use_board
33247  if (pboard) {
33248  const float lp = cimg::min(lightprops(l),1);
33249  board.setPenColorRGBi((unsigned char)(color[0]*lp),
33250  (unsigned char)(color[1]*lp),
33251  (unsigned char)(color[2]*lp),
33252  (unsigned char)(opac*255));
33253  board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33254  }
33255 #endif
33256  break;
33257  case 4 :
33258  if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opac);
33259  else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opac);
33260 #ifdef cimg_use_board
33261  if (pboard) {
33262  board.setPenColorRGBi((unsigned char)(color[0]),
33263  (unsigned char)(color[1]),
33264  (unsigned char)(color[2]),
33265  (unsigned char)(opac*255));
33266  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
33267  (float)x1,height()-(float)y1,lightprops(n1),
33268  (float)x2,height()-(float)y2,lightprops(n2));
33269  }
33270 #endif
33271  break;
33272  case 5 : {
33273  const unsigned int
33274  lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
33275  lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
33276  lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1);
33277  if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac);
33278  else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac);
33279 #ifdef cimg_use_board
33280  if (pboard) {
33281  const float
33282  l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
33283  l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
33284  l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1))));
33285  board.setPenColorRGBi((unsigned char)(color[0]),
33286  (unsigned char)(color[1]),
33287  (unsigned char)(color[2]),
33288  (unsigned char)(opac*255));
33289  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33290  (float)x1,height()-(float)y1,l1,
33291  (float)x2,height()-(float)y2,l2);
33292  }
33293 #endif
33294  } break;
33295  }
33296  } break;
33297  case 4 : { // Colored rectangle
33298  const unsigned int
33299  n0 = (unsigned int)primitive[0],
33300  n1 = (unsigned int)primitive[1],
33301  n2 = (unsigned int)primitive[2],
33302  n3 = (unsigned int)primitive[3];
33303  const int
33304  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33305  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
33306  x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
33307  x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
33308  const float
33309  z0 = vertices(n0,2) + Z + _focale,
33310  z1 = vertices(n1,2) + Z + _focale,
33311  z2 = vertices(n2,2) + Z + _focale,
33312  z3 = vertices(n3,2) + Z + _focale;
33313  switch (render_type) {
33314  case 0 :
33315  draw_point(x0,y0,pcolor,opac).draw_point(x1,y1,pcolor,opac).
33316  draw_point(x2,y2,pcolor,opac).draw_point(x3,y3,pcolor,opac);
33317 #ifdef cimg_use_board
33318  if (pboard) {
33319  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33320  board.drawCircle((float)x0,height()-(float)y0,0);
33321  board.drawCircle((float)x1,height()-(float)y1,0);
33322  board.drawCircle((float)x2,height()-(float)y2,0);
33323  board.drawCircle((float)x3,height()-(float)y3,0);
33324  }
33325 #endif
33326  break;
33327  case 1 :
33328  if (zbuffer)
33329  draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opac).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opac).
33330  draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opac).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opac);
33331  else
33332  draw_line(x0,y0,x1,y1,pcolor,opac).draw_line(x1,y1,x2,y2,pcolor,opac).
33333  draw_line(x2,y2,x3,y3,pcolor,opac).draw_line(x3,y3,x0,y0,pcolor,opac);
33334 #ifdef cimg_use_board
33335  if (pboard) {
33336  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33337  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33338  board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33339  board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33340  board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
33341  }
33342 #endif
33343  break;
33344  case 2 :
33345  if (zbuffer)
33346  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac).draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opac);
33347  else
33348  draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac);
33349 #ifdef cimg_use_board
33350  if (pboard) {
33351  board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opac*255));
33352  board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33353  board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33354  }
33355 #endif
33356  break;
33357  case 3 :
33358  if (zbuffer)
33359  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opac,lightprops(l)).
33360  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opac,lightprops(l));
33361  else
33362  _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opac,lightprops(l)).
33363  _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opac,lightprops(l));
33364 #ifdef cimg_use_board
33365  if (pboard) {
33366  const float lp = cimg::min(lightprops(l),1);
33367  board.setPenColorRGBi((unsigned char)(color[0]*lp),
33368  (unsigned char)(color[1]*lp),
33369  (unsigned char)(color[2]*lp),(unsigned char)(opac*255));
33370  board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33371  board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33372  }
33373 #endif
33374  break;
33375  case 4 : {
33376  const float
33377  lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
33378  lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
33379  if (zbuffer)
33380  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,lightprop0,lightprop1,lightprop2,opac).
33381  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,lightprop0,lightprop2,lightprop3,opac);
33382  else
33383  draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprop0,lightprop1,lightprop2,opac).
33384  draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,lightprop0,lightprop2,lightprop3,opac);
33385 #ifdef cimg_use_board
33386  if (pboard) {
33387  board.setPenColorRGBi((unsigned char)(color[0]),
33388  (unsigned char)(color[1]),
33389  (unsigned char)(color[2]),
33390  (unsigned char)(opac*255));
33391  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
33392  (float)x1,height()-(float)y1,lightprop1,
33393  (float)x2,height()-(float)y2,lightprop2);
33394  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
33395  (float)x2,height()-(float)y2,lightprop2,
33396  (float)x3,height()-(float)y3,lightprop3);
33397  }
33398 #endif
33399  } break;
33400  case 5 : {
33401  const unsigned int
33402  lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
33403  lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
33404  lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
33405  lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
33406  if (zbuffer)
33407  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
33408  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
33409  else
33410  draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
33411  draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
33412 #ifdef cimg_use_board
33413  if (pboard) {
33414  const float
33415  l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
33416  l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
33417  l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
33418  l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
33419  board.setPenColorRGBi((unsigned char)(color[0]),
33420  (unsigned char)(color[1]),
33421  (unsigned char)(color[2]),
33422  (unsigned char)(opac*255));
33423  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33424  (float)x1,height()-(float)y1,l1,
33425  (float)x2,height()-(float)y2,l2);
33426  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33427  (float)x2,height()-(float)y2,l2,
33428  (float)x3,height()-(float)y3,l3);
33429  }
33430 #endif
33431  } break;
33432  }
33433  } break;
33434  case 9 : { // Textured triangle
33435  if (!__color)
33436  throw CImgArgumentException(_cimg_instance
33437  "draw_object3d(): Undefined texture for triangle primitive [%u].",
33438  cimg_instance,n_primitive);
33439  const unsigned int
33440  n0 = (unsigned int)primitive[0],
33441  n1 = (unsigned int)primitive[1],
33442  n2 = (unsigned int)primitive[2],
33443  tx0 = (unsigned int)primitive[3],
33444  ty0 = (unsigned int)primitive[4],
33445  tx1 = (unsigned int)primitive[5],
33446  ty1 = (unsigned int)primitive[6],
33447  tx2 = (unsigned int)primitive[7],
33448  ty2 = (unsigned int)primitive[8];
33449  const int
33450  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33451  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
33452  x2 = (int)projections(n2,0), y2 = (int)projections(n2,1);
33453  const float
33454  z0 = vertices(n0,2) + Z + _focale,
33455  z1 = vertices(n1,2) + Z + _focale,
33456  z2 = vertices(n2,2) + Z + _focale;
33457  switch (render_type) {
33458  case 0 :
33459  draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
33460  draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac).
33461  draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac);
33462 #ifdef cimg_use_board
33463  if (pboard) {
33464  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33465  board.drawCircle((float)x0,height()-(float)y0,0);
33466  board.drawCircle((float)x1,height()-(float)y1,0);
33467  board.drawCircle((float)x2,height()-(float)y2,0);
33468  }
33469 #endif
33470  break;
33471  case 1 :
33472  if (zbuffer)
33473  draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
33474  draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac).
33475  draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac);
33476  else
33477  draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
33478  draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opac).
33479  draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac);
33480 #ifdef cimg_use_board
33481  if (pboard) {
33482  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33483  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33484  board.drawLine((float)x0,height()-(float)y0,(float)x2,height()-(float)y2);
33485  board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33486  }
33487 #endif
33488  break;
33489  case 2 :
33490  if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac);
33491  else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac);
33492 #ifdef cimg_use_board
33493  if (pboard) {
33494  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33495  board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33496  }
33497 #endif
33498  break;
33499  case 3 :
33500  if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l));
33501  else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l));
33502 #ifdef cimg_use_board
33503  if (pboard) {
33504  const float lp = cimg::min(lightprops(l),1);
33505  board.setPenColorRGBi((unsigned char)(128*lp),
33506  (unsigned char)(128*lp),
33507  (unsigned char)(128*lp),
33508  (unsigned char)(opac*255));
33509  board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33510  }
33511 #endif
33512  break;
33513  case 4 :
33514  if (zbuffer)
33515  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac);
33516  else
33517  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprops(n0),lightprops(n1),lightprops(n2),opac);
33518 #ifdef cimg_use_board
33519  if (pboard) {
33520  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33521  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprops(n0),
33522  (float)x1,height()-(float)y1,lightprops(n1),
33523  (float)x2,height()-(float)y2,lightprops(n2));
33524  }
33525 #endif
33526  break;
33527  case 5 :
33528  if (zbuffer)
33529  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
33530  (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
33531  (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
33532  (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
33533  opac);
33534  else
33535  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
33536  (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
33537  (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
33538  (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
33539  opac);
33540 #ifdef cimg_use_board
33541  if (pboard) {
33542  const float
33543  l0 = light_texture((int)(light_texture.width()/2*(1+lightprops(n0,0))), (int)(light_texture.height()/2*(1+lightprops(n0,1)))),
33544  l1 = light_texture((int)(light_texture.width()/2*(1+lightprops(n1,0))), (int)(light_texture.height()/2*(1+lightprops(n1,1)))),
33545  l2 = light_texture((int)(light_texture.width()/2*(1+lightprops(n2,0))), (int)(light_texture.height()/2*(1+lightprops(n2,1))));
33546  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33547  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,(float)x1,height()-(float)y1,l1,(float)x2,height()-(float)y2,l2);
33548  }
33549 #endif
33550  break;
33551  }
33552  } break;
33553  case 12 : { // Textured quadrangle
33554  if (!__color)
33555  throw CImgArgumentException(_cimg_instance
33556  "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
33557  cimg_instance,n_primitive);
33558  const unsigned int
33559  n0 = (unsigned int)primitive[0],
33560  n1 = (unsigned int)primitive[1],
33561  n2 = (unsigned int)primitive[2],
33562  n3 = (unsigned int)primitive[3],
33563  tx0 = (unsigned int)primitive[4],
33564  ty0 = (unsigned int)primitive[5],
33565  tx1 = (unsigned int)primitive[6],
33566  ty1 = (unsigned int)primitive[7],
33567  tx2 = (unsigned int)primitive[8],
33568  ty2 = (unsigned int)primitive[9],
33569  tx3 = (unsigned int)primitive[10],
33570  ty3 = (unsigned int)primitive[11];
33571  const int
33572  x0 = (int)projections(n0,0), y0 = (int)projections(n0,1),
33573  x1 = (int)projections(n1,0), y1 = (int)projections(n1,1),
33574  x2 = (int)projections(n2,0), y2 = (int)projections(n2,1),
33575  x3 = (int)projections(n3,0), y3 = (int)projections(n3,1);
33576  const float
33577  z0 = vertices(n0,2) + Z + _focale,
33578  z1 = vertices(n1,2) + Z + _focale,
33579  z2 = vertices(n2,2) + Z + _focale,
33580  z3 = vertices(n3,2) + Z + _focale;
33581 
33582  switch (render_type) {
33583  case 0 :
33584  draw_point(x0,y0,color.get_vector_at(tx0,ty0)._data,opac).
33585  draw_point(x1,y1,color.get_vector_at(tx1,ty1)._data,opac).
33586  draw_point(x2,y2,color.get_vector_at(tx2,ty2)._data,opac).
33587  draw_point(x3,y3,color.get_vector_at(tx3,ty3)._data,opac);
33588 #ifdef cimg_use_board
33589  if (pboard) {
33590  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33591  board.drawCircle((float)x0,height()-(float)y0,0);
33592  board.drawCircle((float)x1,height()-(float)y1,0);
33593  board.drawCircle((float)x2,height()-(float)y2,0);
33594  board.drawCircle((float)x3,height()-(float)y3,0);
33595  }
33596 #endif
33597  break;
33598  case 1 :
33599  if (zbuffer)
33600  draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
33601  draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac).
33602  draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac).
33603  draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac);
33604  else
33605  draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opac).
33606  draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opac).
33607  draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opac).
33608  draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opac);
33609 #ifdef cimg_use_board
33610  if (pboard) {
33611  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33612  board.drawLine((float)x0,height()-(float)y0,(float)x1,height()-(float)y1);
33613  board.drawLine((float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33614  board.drawLine((float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33615  board.drawLine((float)x3,height()-(float)y3,(float)x0,height()-(float)y0);
33616  }
33617 #endif
33618  break;
33619  case 2 :
33620  if (zbuffer)
33621  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac).
33622  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac);
33623  else
33624  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac).
33625  draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac);
33626 #ifdef cimg_use_board
33627  if (pboard) {
33628  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33629  board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33630  board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33631  }
33632 #endif
33633  break;
33634  case 3 :
33635  if (zbuffer)
33636  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)).
33637  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l));
33638  else
33639  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opac,lightprops(l)).
33640  draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opac,lightprops(l));
33641 #ifdef cimg_use_board
33642  if (pboard) {
33643  const float lp = cimg::min(lightprops(l),1);
33644  board.setPenColorRGBi((unsigned char)(128*lp),
33645  (unsigned char)(128*lp),
33646  (unsigned char)(128*lp),
33647  (unsigned char)(opac*255));
33648  board.fillTriangle((float)x0,height()-(float)y0,(float)x1,height()-(float)y1,(float)x2,height()-(float)y2);
33649  board.fillTriangle((float)x0,height()-(float)y0,(float)x2,height()-(float)y2,(float)x3,height()-(float)y3);
33650  }
33651 #endif
33652  break;
33653  case 4 : {
33654  const float
33655  lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
33656  lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
33657  if (zbuffer)
33658  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac).
33659  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac);
33660  else
33661  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,lightprop0,lightprop1,lightprop2,opac).
33662  draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,lightprop0,lightprop2,lightprop3,opac);
33663 #ifdef cimg_use_board
33664  if (pboard) {
33665  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33666  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
33667  (float)x1,height()-(float)y1,lightprop1,
33668  (float)x2,height()-(float)y2,lightprop2);
33669  board.fillGouraudTriangle((float)x0,height()-(float)y0,lightprop0,
33670  (float)x2,height()-(float)y2,lightprop2,
33671  (float)x3,height()-(float)y3,lightprop3);
33672  }
33673 #endif
33674  } break;
33675  case 5 : {
33676  const unsigned int
33677  lx0 = (unsigned int)lightprops(n0,0), ly0 = (unsigned int)lightprops(n0,1),
33678  lx1 = (unsigned int)lightprops(n1,0), ly1 = (unsigned int)lightprops(n1,1),
33679  lx2 = (unsigned int)lightprops(n2,0), ly2 = (unsigned int)lightprops(n2,1),
33680  lx3 = (unsigned int)lightprops(n3,0), ly3 = (unsigned int)lightprops(n3,1);
33681  if (zbuffer)
33682  draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
33683  draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
33684  else
33685  draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opac).
33686  draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opac);
33687 #ifdef cimg_use_board
33688  if (pboard) {
33689  const float
33690  l0 = light_texture((int)(light_texture.width()/2*(1+lx0)), (int)(light_texture.height()/2*(1+ly0))),
33691  l1 = light_texture((int)(light_texture.width()/2*(1+lx1)), (int)(light_texture.height()/2*(1+ly1))),
33692  l2 = light_texture((int)(light_texture.width()/2*(1+lx2)), (int)(light_texture.height()/2*(1+ly2))),
33693  l3 = light_texture((int)(light_texture.width()/2*(1+lx3)), (int)(light_texture.height()/2*(1+ly3)));
33694  board.setPenColorRGBi(128,128,128,(unsigned char)(opac*255));
33695  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33696  (float)x1,height()-(float)y1,l1,
33697  (float)x2,height()-(float)y2,l2);
33698  board.fillGouraudTriangle((float)x0,height()-(float)y0,l0,
33699  (float)x2,height()-(float)y2,l2,
33700  (float)x3,height()-(float)y3,l3);
33701  }
33702 #endif
33703  } break;
33704  }
33705  } break;
33706  }
33707  }
33708  return *this;
33709  }
33710 
33712  //---------------------------
33713  //
33715 
33716  //---------------------------
33717 
33719 
33725  const unsigned int feature_type=2, unsigned int *const XYZ=0) {
33726  return get_select(disp,feature_type,XYZ).move_to(*this);
33727  }
33728 
33730  CImg<T>& select(const char *const title,
33731  const unsigned int feature_type=2, unsigned int *const XYZ=0) {
33732  return get_select(title,feature_type,XYZ).move_to(*this);
33733  }
33734 
33737  const unsigned int feature_type=2, unsigned int *const XYZ=0) const {
33738  return _get_select(disp,0,feature_type,XYZ,0,0,0,true);
33739  }
33740 
33742  CImg<intT> get_select(const char *const title,
33743  const unsigned int feature_type=2, unsigned int *const XYZ=0) const {
33744  CImgDisplay disp;
33745  return _get_select(disp,title,feature_type,XYZ,0,0,0,true);
33746  }
33747 
33748  CImg<intT> _get_select(CImgDisplay &disp, const char *const title,
33749  const unsigned int feature_type, unsigned int *const XYZ,
33750  const int origX, const int origY, const int origZ,
33751  const bool reset_view3d=true) const {
33752  if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
33753  if (!disp) {
33754  disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
33755  if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
33756  } else if (title) disp.set_title("%s",title);
33757 
33758  const unsigned int old_normalization = disp.normalization();
33759  bool old_is_resized = disp.is_resized();
33760  disp._normalization = 0;
33761  disp.show().set_key(0).set_wheel();
33762 
33763  unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
33764 
33765  int area = 0, starting_area = 0, clicked_area = 0, phase = 0,
33766  X0 = (int)((XYZ?XYZ[0]:_width/2)%_width), Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height), Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth),
33767  X1 =-1, Y1 = -1, Z1 = -1,
33768  X = -1, Y = -1, Z = -1, X3d = -1, Y3d = -1,
33769  oX = X, oY = Y, oZ = Z, oX3d = X3d, oY3d = -1;
33770  unsigned int old_button = 0, key = 0;
33771 
33772  bool shape_selected = false, text_down = false;
33773  static CImg<floatT> pose3d;
33774  static bool is_view3d = false;
33775  if (reset_view3d) { pose3d.assign(); is_view3d = false; }
33776  CImg<floatT> points3d, opacities3d, sel_opacities3d;
33777  CImgList<uintT> primitives3d, sel_primitives3d;
33778  CImgList<ucharT> colors3d, sel_colors3d;
33779  CImg<ucharT> visu, visu0, view3d;
33780  char text[1024] = { 0 };
33781 
33782  while (!key && !disp.is_closed() && !shape_selected) {
33783 
33784  // Handle mouse motion and selection
33785  oX = X; oY = Y; oZ = Z;
33786  int
33787  mx = disp.mouse_x(),
33788  my = disp.mouse_y();
33789  const int
33790  mX = mx<0?-1:mx*(width()+(depth()>1?depth():0))/disp.width(),
33791  mY = my<0?-1:my*(height()+(depth()>1?depth():0))/disp.height();
33792  area = 0;
33793  if (mX>=0 && mY>=0 && mX<width() && mY<height()) { area = 1; X = mX; Y = mY; Z = phase?Z1:Z0; }
33794  if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = phase?Y1:Y0; }
33795  if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = phase?X1:X0; }
33796  if (mX>=width() && mY>=height()) area = 4;
33797  if (disp.button()) { if (!clicked_area) clicked_area = area; } else clicked_area = 0;
33798 
33799  switch (key = disp.key()) {
33800 #if cimg_OS!=2
33801  case cimg::keyCTRLRIGHT :
33802 #endif
33803  case 0 : case cimg::keyCTRLLEFT : key = 0; break;
33804  case cimg::keyPAGEUP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
33805  case cimg::keyPAGEDOWN : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
33806  case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33807  disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
33808  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
33809  _is_resized = true;
33810  disp.set_key(key,false); key = 0; visu0.assign();
33811  } break;
33812  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33813  disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
33814  disp.set_key(key,false); key = 0; visu0.assign();
33815  } break;
33816  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33817  disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
33818  disp.set_key(key,false); key = 0; visu0.assign();
33819  } break;
33820  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33821  disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
33822  disp.set_key(key,false); key = 0; visu0.assign();
33823  } break;
33824  case cimg::keyV : is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign(); break;
33825  case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33826  static unsigned int snap_number = 0;
33827  char filename[32] = { 0 };
33828  std::FILE *file;
33829  do {
33830  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
33831  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
33832  } while (file);
33833  if (visu0) {
33834  visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp);
33835  visu0.save(filename);
33836  visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
33837  }
33838  disp.set_key(key,false); key = 0;
33839  } break;
33840  case cimg::keyO :
33841  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
33842  static unsigned int snap_number = 0;
33843  char filename[32] = { 0 };
33844  std::FILE *file;
33845  do {
33846 #ifdef cimg_use_zlib
33847  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
33848 #else
33849  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
33850 #endif
33851  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
33852  } while (file);
33853  visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp);
33854  save(filename);
33855  visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
33856  disp.set_key(key,false); key = 0;
33857  } break;
33858  }
33859 
33860  switch (area) {
33861 
33862  case 0 : // When mouse is out of image range.
33863  mx = my = X = Y = Z = -1;
33864  break;
33865 
33866  case 1 : case 2 : case 3 : // When mouse is over the XY,XZ or YZ projections.
33867  if (disp.button()&1 && phase<2 && clicked_area==area) { // When selection has been started (1st step).
33868  if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign();
33869  X1 = X; Y1 = Y; Z1 = Z;
33870  }
33871  if (!(disp.button()&1) && phase>=2 && clicked_area!=area) { // When selection is at 2nd step (for volumes).
33872  switch (starting_area) {
33873  case 1 : if (Z1!=Z) visu0.assign(); Z1 = Z; break;
33874  case 2 : if (Y1!=Y) visu0.assign(); Y1 = Y; break;
33875  case 3 : if (X1!=X) visu0.assign(); X1 = X; break;
33876  }
33877  }
33878  if (disp.button()&2 && clicked_area==area) { // When moving through the image/volume.
33879  if (phase) {
33880  if (_depth>1 && (X1!=X || Y1!=Y || Z1!=Z)) visu0.assign();
33881  X1 = X; Y1 = Y; Z1 = Z;
33882  } else {
33883  if (_depth>1 && (X0!=X || Y0!=Y || Z0!=Z)) visu0.assign();
33884  X0 = X; Y0 = Y; Z0 = Z;
33885  }
33886  }
33887  if (disp.button()&4) { // Reset positions.
33888  oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = area = clicked_area = starting_area = 0; visu0.assign();
33889  }
33890  if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel).
33891  if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() && !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT() &&
33892  !disp.is_keyALT() && !disp.is_keyALTGR()) {
33893  switch (area) {
33894  case 1 :
33895  if (phase) Z = (Z1+=disp.wheel()); else Z = (Z0+=disp.wheel());
33896  visu0.assign(); break;
33897  case 2 :
33898  if (phase) Y = (Y1+=disp.wheel()); else Y = (Y0+=disp.wheel());
33899  visu0.assign(); break;
33900  case 3 :
33901  if (phase) X = (X1+=disp.wheel()); else X = (X0+=disp.wheel());
33902  visu0.assign(); break;
33903  }
33904  disp.set_wheel();
33905  } else key = ~0U;
33906  }
33907  if ((disp.button()&1)!=old_button) { // When left button has just been pressed or released.
33908  switch (phase) {
33909  case 0 :
33910  if (area==clicked_area) {
33911  X0 = X1 = X; Y0 = Y1 = Y; Z0 = Z1 = Z; starting_area = area; ++phase;
33912  } break;
33913  case 1 :
33914  if (area==starting_area) {
33915  X1 = X; Y1 = Y; Z1 = Z; ++phase;
33916  } else if (!(disp.button()&1)) { oX = X = X0; oY = Y = Y0; oZ = Z = Z0; phase = 0; visu0.assign(); }
33917  break;
33918  case 2 : ++phase; break;
33919  }
33920  old_button = disp.button()&1;
33921  }
33922  break;
33923 
33924  case 4 : // When mouse is over the 3d view.
33925  if (is_view3d && points3d) {
33926  X3d = mx - _width*disp.width()/(_width+(_depth>1?_depth:0));
33927  Y3d = my - _height*disp.height()/(_height+(_depth>1?_depth:0));
33928  if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
33929  if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; } // Left + right buttons: reset.
33930  else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate.
33931  const float
33932  R = 0.45f*cimg::min(view3d._width,view3d._height),
33933  R2 = R*R,
33934  u0 = (float)(oX3d-view3d.width()/2),
33935  v0 = (float)(oY3d-view3d.height()/2),
33936  u1 = (float)(X3d-view3d.width()/2),
33937  v1 = (float)(Y3d-view3d.height()/2),
33938  n0 = (float)std::sqrt(u0*u0+v0*v0),
33939  n1 = (float)std::sqrt(u1*u1+v1*v1),
33940  nu0 = n0>R?(u0*R/n0):u0,
33941  nv0 = n0>R?(v0*R/n0):v0,
33942  nw0 = (float)std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)),
33943  nu1 = n1>R?(u1*R/n1):u1,
33944  nv1 = n1>R?(v1*R/n1):v1,
33945  nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)),
33946  u = nv0*nw1 - nw0*nv1,
33947  v = nw0*nu1 - nu0*nw1,
33948  w = nv0*nu1 - nu0*nv1,
33949  n = (float)std::sqrt(u*u+v*v+w*w),
33950  alpha = (float)std::asin(n/R2);
33951  pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,alpha)*pose3d.get_crop(0,0,2,2));
33952  view3d.assign();
33953  } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom.
33954  pose3d(3,2)-=(oY3d - Y3d)*1.5f; view3d.assign();
33955  }
33956  if (disp.wheel()) { // Wheel: zoom
33957  pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
33958  }
33959  if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift.
33960  pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
33961  }
33962  oX3d = X3d; oY3d = Y3d;
33963  }
33964  mx = my = X = Y = Z = -1;
33965  break;
33966  }
33967 
33968  if (phase) {
33969  if (!feature_type) shape_selected = phase?true:false;
33970  else {
33971  if (_depth>1) shape_selected = (phase==3)?true:false;
33972  else shape_selected = (phase==2)?true:false;
33973  }
33974  }
33975 
33976  if (X0<0) X0 = 0; if (X0>=width()) X0 = width() - 1;
33977  if (Y0<0) Y0 = 0; if (Y0>=height()) Y0 = height() - 1;
33978  if (Z0<0) Z0 = 0; if (Z0>=depth()) Z0 = depth() - 1;
33979  if (X1<1) X1 = 0; if (X1>=width()) X1 = width() - 1;
33980  if (Y1<0) Y1 = 0; if (Y1>=height()) Y1 = height() - 1;
33981  if (Z1<0) Z1 = 0; if (Z1>=depth()) Z1 = depth() - 1;
33982 
33983  // Draw visualization image on the display
33984  if (oX!=X || oY!=Y || oZ!=Z || !visu0 || (_depth>1 && !view3d)) {
33985 
33986  if (!visu0) { // Create image of projected planes.
33987  CImg<Tuchar> tmp, tmp0;
33988  if (_depth!=1) {
33989  tmp0 = get_projections2d(phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0);
33990  tmp = tmp0.get_channels(0,cimg::min(2,spectrum() - 1));
33991  } else tmp = get_channels(0,cimg::min(2,spectrum() - 1));
33992  switch (old_normalization) {
33993  case 0 : tmp.move_to(visu0); break;
33994  case 1 : tmp.normalize(0,255).move_to(visu0); break;
33995  case 2 : {
33996  const float m = disp._min, M = disp._max;
33997  ((tmp-=m)*=255.0f/(M-m>0?M-m:1)).move_to(visu0);
33998  }
33999  case 3 :
34000  if (cimg::type<T>::is_float()) (tmp.normalize(0,255)).move_to(visu0);
34001  else {
34002  const float m = (float)cimg::type<T>::min(), M = (float)cimg::type<T>::max();
34003  ((tmp-=m)*=255.0f/(M-m)).move_to(visu0);
34004  } break;
34005  }
34006  visu0.resize(disp);
34007  view3d.assign();
34008  points3d.assign();
34009  }
34010 
34011  if (is_view3d && _depth>1 && !view3d) { // Create 3d view for volumetric images.
34012  const unsigned int
34013  _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width+_depth),1,1),
34014  _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height+_depth),1,1),
34015  x3d = _x3d>=visu0._width?visu0._width-1:_x3d,
34016  y3d = _y3d>=visu0._height?visu0._height-1:_y3d;
34017  CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width-x3d,visu0._height-y3d,1,visu0._spectrum,3).move_to(view3d);
34018  if (!points3d) {
34019  get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
34020  points3d.append(CImg<floatT>(8,3,1,1,
34021  0,_width-1,_width-1,0,0,_width-1,_width-1,0,
34022  0,0,_height-1,_height-1,0,0,_height-1,_height-1,
34023  0,0,0,0,_depth-1,_depth-1,_depth-1,_depth-1),'x');
34024  CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
34025  CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
34026  CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
34027  CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
34028  CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
34029  CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
34030  colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
34031  opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
34032  if (!phase) {
34033  opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
34034  sel_primitives3d.assign();
34035  sel_colors3d.assign();
34036  sel_opacities3d.assign();
34037  } else {
34038  if (feature_type==2) {
34039  points3d.append(CImg<floatT>(8,3,1,1,
34040  X0,X1,X1,X0,X0,X1,X1,X0,
34041  Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
34042  Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
34043  sel_primitives3d.assign();
34044  CImg<uintT>::vector(20,21).move_to(sel_primitives3d); CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
34045  CImg<uintT>::vector(22,23).move_to(sel_primitives3d); CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
34046  CImg<uintT>::vector(24,25).move_to(sel_primitives3d); CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
34047  CImg<uintT>::vector(26,27).move_to(sel_primitives3d); CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
34048  CImg<uintT>::vector(20,24).move_to(sel_primitives3d); CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
34049  CImg<uintT>::vector(22,26).move_to(sel_primitives3d); CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
34050  } else {
34051  points3d.append(CImg<floatT>(2,3,1,1,
34052  X0,X1,
34053  Y0,Y1,
34054  Z0,Z1),'x');
34055  sel_primitives3d.assign(CImg<uintT>::vector(20,21));
34056  }
34057  sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
34058  sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
34059  }
34060  points3d.shift_object3d(-0.5f*_width,-0.5f*_height,-0.5f*_depth).resize_object3d();
34061  points3d*=0.75f*cimg::min(view3d._width,view3d._height);
34062  }
34063 
34064  if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
34065  CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
34066  const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
34067  if (sel_primitives3d)
34068  view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
34069  pose3d(3,1) + 0.5f*view3d._height,
34070  pose3d(3,2),
34071  rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
34072  2,true,500,0,0,0,0,0,zbuffer3d);
34073  view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
34074  pose3d(3,1) + 0.5f*view3d._height,
34075  pose3d(3,2),
34076  rotated_points3d,primitives3d,colors3d,opacities3d,
34077  2,true,500,0,0,0,0,0,zbuffer3d);
34078  visu0.draw_image(x3d,y3d,view3d);
34079  }
34080  visu = visu0;
34081 
34082  const int d = (_depth>1)?_depth:0;
34083  if (phase) switch (feature_type) {
34084  case 1 : {
34085  const int
34086  x0 = (int)((X0+0.5f)*disp.width()/(_width+d)),
34087  y0 = (int)((Y0+0.5f)*disp.height()/(_height+d)),
34088  x1 = (int)((X1+0.5f)*disp.width()/(_width+d)),
34089  y1 = (int)((Y1+0.5f)*disp.height()/(_height+d));
34090  visu.draw_arrow(x0,y0,x1,y1,background_color,0.9f,30,5,0x55555555).
34091  draw_arrow(x0,y0,x1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA);
34092  if (d) {
34093  const int
34094  zx0 = (int)((_width+Z0+0.5f)*disp.width()/(_width+d)),
34095  zx1 = (int)((_width+Z1+0.5f)*disp.width()/(_width+d)),
34096  zy0 = (int)((_height+Z0+0.5f)*disp.height()/(_height+d)),
34097  zy1 = (int)((_height+Z1+0.5f)*disp.height()/(_height+d));
34098  visu.draw_arrow(zx0,y0,zx1,y1,foreground_color,0.9f,30,5,0x55555555).
34099  draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0x55555555).
34100  draw_arrow(zx0,y0,zx1,y1,foreground_color,0.9f,30,5,0xAAAAAAAA).
34101  draw_arrow(x0,zy0,x1,zy1,foreground_color,0.9f,30,5,0xAAAAAAAA);
34102  }
34103  } break;
34104  case 2 : {
34105  const int
34106  x0 = (X0<X1?X0:X1)*disp.width()/(_width+d),
34107  y0 = (Y0<Y1?Y0:Y1)*disp.height()/(_height+d),
34108  x1 = ((X0<X1?X1:X0)+1)*disp.width()/(_width+d)-1,
34109  y1 = ((Y0<Y1?Y1:Y0)+1)*disp.height()/(_height+d)-1;
34110  visu.draw_rectangle(x0,y0,x1,y1,background_color,0.2f).draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,0x55555555);
34111  if (d) {
34112  const int
34113  zx0 = (int)((_width+(Z0<Z1?Z0:Z1))*disp.width()/(_width+d)),
34114  zy0 = (int)((_height+(Z0<Z1?Z0:Z1))*disp.height()/(_height+d)),
34115  zx1 = (int)((_width+(Z0<Z1?Z1:Z0)+1)*disp.width()/(_width+d))-1,
34116  zy1 = (int)((_height+(Z0<Z1?Z1:Z0)+1)*disp.height()/(_height+d))-1;
34117  visu.draw_rectangle(zx0,y0,zx1,y1,background_color,0.2f).draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.6f,0x55555555).
34118  draw_rectangle(x0,zy0,x1,zy1,background_color,0.2f).draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.6f,0x55555555);
34119  }
34120  } break;
34121  case 3 : {
34122  const int
34123  x0 = X0*disp.width()/(_width+d),
34124  y0 = Y0*disp.height()/(_height+d),
34125  x1 = X1*disp.width()/(_width+d)-1,
34126  y1 = Y1*disp.height()/(_height+d)-1;
34127  visu.draw_ellipse(x0,y0,(float)cimg::abs(x1-x0),(float)cimg::abs(y1-y0),0,background_color,0.2f).
34128  draw_ellipse(x0,y0,(float)cimg::abs(x1-x0),(float)cimg::abs(y1-y0),0,foreground_color,0.6f,0x55555555).
34129  draw_point(x0,y0,foreground_color,0.6f);
34130  if (d) {
34131  const int
34132  zx0 = (int)((_width+Z0)*disp.width()/(_width+d)),
34133  zy0 = (int)((_height+Z0)*disp.height()/(_height+d)),
34134  zx1 = (int)((_width+Z1+1)*disp.width()/(_width+d))-1,
34135  zy1 = (int)((_height+Z1+1)*disp.height()/(_height+d))-1;
34136  visu.draw_ellipse(zx0,y0,(float)cimg::abs(zx1-zx0),(float)cimg::abs(y1-y0),0,background_color,0.2f).
34137  draw_ellipse(zx0,y0,(float)cimg::abs(zx1-zx0),(float)cimg::abs(y1-y0),0,foreground_color,0.6f,0x55555555).
34138  draw_point(zx0,y0,foreground_color,0.6f).
34139  draw_ellipse(x0,zy0,(float)cimg::abs(x1-x0),(float)cimg::abs(zy1-zy0),0,background_color,0.2f).
34140  draw_ellipse(x0,zy0,(float)cimg::abs(x1-x0),(float)cimg::abs(zy1-zy0),0,foreground_color,0.6f,0x55555555).
34141  draw_point(x0,zy0,foreground_color,0.6f);
34142  }
34143  } break;
34144  } else {
34145  const int
34146  x0 = X*disp.width()/(_width+d),
34147  y0 = Y*disp.height()/(_height+d),
34148  x1 = (X+1)*disp.width()/(_width+d)-1,
34149  y1 = (Y+1)*disp.height()/(_height+d)-1,
34150  zx0 = (Z+_width)*disp.width()/(_width+d),
34151  zx1 = (Z+_width+1)*disp.width()/(_width+d),
34152  zy0 = (Z+_height)*disp.height()/(_height+d),
34153  zy1 = (Z+_height+1)*disp.height()/(_height+d);
34154 
34155  if (x1-x0>=4 && y1-y0>=4) visu.draw_rectangle(x0,y0,x1,y1,background_color,0.2f).
34156  draw_rectangle(x0,y0,x1,y1,foreground_color,0.6f,~0U);
34157 
34158  if (_depth>1) {
34159  if (y1-y0>=4 && zx1-zx0>=4) visu.draw_rectangle(zx0,y0,zx1,y1,background_color,0.2f).
34160  draw_rectangle(zx0,y0,zx1,y1,foreground_color,0.6f,~0U);
34161  if (x1-x0>=4 && zy1-zy0>=4) visu.draw_rectangle(x0,zy0,x1,zy1,background_color,0.2f).
34162  draw_rectangle(x0,zy0,x1,zy1,foreground_color,0.6f,~0U);
34163  }
34164  }
34165 
34166  if (my>=0 && my<13) text_down = true; else if (my>=visu.height()-13) text_down = false;
34167  if (!feature_type || !phase) {
34168  if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
34169  if (_depth>1) cimg_snprintf(text,sizeof(text)," Point (%d,%d,%d) = [ ",origX+X,origY+Y,origZ+Z);
34170  else cimg_snprintf(text,sizeof(text)," Point (%d,%d) = [ ",origX+X,origY+Y);
34171  char *ctext = text + std::strlen(text), *const ltext = text + 512;
34172  for (unsigned int c = 0; c<_spectrum && ctext<ltext; ++c) {
34173  cimg_snprintf(ctext,sizeof(text)/2,cimg::type<T>::format(),cimg::type<T>::format((*this)(X,Y,Z,c)));
34174  ctext = text + std::strlen(text);
34175  *(ctext++) = ' '; *ctext = 0;
34176  }
34177  std::strcpy(text + std::strlen(text),"] ");
34178  }
34179  } else switch (feature_type) {
34180  case 1 : {
34181  const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1), norm = std::sqrt(dX*dX+dY*dY+dZ*dZ);
34182  if (_depth>1) cimg_snprintf(text,sizeof(text)," Vect (%d,%d,%d)-(%d,%d,%d), Norm = %g ",
34183  origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,norm);
34184  else cimg_snprintf(text,sizeof(text)," Vect (%d,%d)-(%d,%d), Norm = %g ",
34185  origX+X0,origY+Y0,origX+X1,origY+Y1,norm);
34186  } break;
34187  case 2 :
34188  if (_depth>1) cimg_snprintf(text,sizeof(text)," Box (%d,%d,%d)-(%d,%d,%d), Size = (%d,%d,%d) ",
34189  origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origZ+(Z0<Z1?Z0:Z1),
34190  origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),origZ+(Z0<Z1?Z1:Z0),
34191  1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
34192  else cimg_snprintf(text,sizeof(text)," Box (%d,%d)-(%d,%d), Size = (%d,%d) ",
34193  origX+(X0<X1?X0:X1),origY+(Y0<Y1?Y0:Y1),origX+(X0<X1?X1:X0),origY+(Y0<Y1?Y1:Y0),
34194  1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
34195  break;
34196  default :
34197  if (_depth>1) cimg_snprintf(text,sizeof(text)," Ellipse (%d,%d,%d)-(%d,%d,%d), Radii = (%d,%d,%d) ",
34198  origX+X0,origY+Y0,origZ+Z0,origX+X1,origY+Y1,origZ+Z1,
34199  1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1),1+cimg::abs(Z0-Z1));
34200  else cimg_snprintf(text,sizeof(text)," Ellipse (%d,%d)-(%d,%d), Radii = (%d,%d) ",
34201  origX+X0,origY+Y0,origX+X1,origY+Y1,1+cimg::abs(X0-X1),1+cimg::abs(Y0-Y1));
34202  }
34203  if (phase || (mx>=0 && my>=0)) visu.draw_text(0,text_down?visu.height()-13:0,text,foreground_color,background_color,0.7f,13);
34204  disp.display(visu).wait();
34205  } else if (!shape_selected) disp.wait();
34206  if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
34207  }
34208 
34209  // Return result
34210  CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
34211  if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
34212  if (shape_selected) {
34213  if (feature_type==2) {
34214  if (X0>X1) cimg::swap(X0,X1);
34215  if (Y0>Y1) cimg::swap(Y0,Y1);
34216  if (Z0>Z1) cimg::swap(Z0,Z1);
34217  }
34218  if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
34219  switch (feature_type) {
34220  case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
34221  case 3 : res[3] = cimg::abs(X1-X0); res[4] = cimg::abs(Y1-Y0); res[5] = cimg::abs(Z1-Z0); // keep no break here!
34222  default : res[0] = X0; res[1] = Y0; res[2] = Z0;
34223  }
34224  }
34225  disp.set_button();
34226  disp._normalization = old_normalization;
34227  disp._is_resized = old_is_resized;
34228  if (key!=~0U) disp.set_key(key);
34229  return res;
34230  }
34231 
34234  const unsigned int plot_type=1, const unsigned int vertex_type=1,
34235  const char *const labelx=0, const double xmin=0, const double xmax=0,
34236  const char *const labely=0, const double ymin=0, const double ymax=0) const {
34237  if (is_empty())
34238  throw CImgInstanceException(_cimg_instance
34239  "select_graph(): Empty instance.",
34240  cimg_instance);
34241  if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type());
34242  const unsigned long siz = (unsigned long)_width*_height*_depth;
34243  const unsigned int old_normalization = disp.normalization();
34244  disp.show().set_button().set_wheel()._normalization = 0;
34245 
34246  double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
34247  if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
34248  if (nymin==nymax) { --nymin; ++nymax; }
34249  if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.0; }
34250 
34251  const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
34252  const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
34253  static unsigned int odimv = 0;
34254  static CImg<ucharT> colormap;
34255  if (odimv!=_spectrum) {
34256  odimv = _spectrum;
34257  colormap = CImg<ucharT>(3,_spectrum,1,1,120).noise(70,1);
34258  if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
34259  else {
34260  colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
34261  if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; }
34262  if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; }
34263  }
34264  }
34265 
34266  CImg<ucharT> visu0, visu, graph, text, axes;
34267  int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
34268  const unsigned int one = plot_type==3?0:1;
34269  unsigned int okey = 0, obutton = 0;
34270  char message[1024] = { 0 };
34271  CImg_3x3(I,unsigned char);
34272 
34273  for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
34274  const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
34275  const unsigned int key = disp.key(), button = disp.button();
34276 
34277  // Generate graph representation.
34278  if (!visu0) {
34279  visu0.assign(disp.width(),disp.height(),1,3,220);
34280  const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
34281  if (gdimx>0 && gdimy>0) {
34282  graph.assign(gdimx,gdimy,1,3,255);
34283  if (siz<32) { if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,false,true,black,0.2f,0x33333333,0x33333333); }
34284  else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
34285  cimg_forC(*this,c) graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
34286  plot_type,vertex_type,nymax,nymin);
34287 
34288  axes.assign(gdimx,gdimy,1,1,0);
34289  const float
34290  dx = (float)cimg::abs(nxmax-nxmin), dy = (float)cimg::abs(nymax-nymin),
34291  px = (float)std::pow(10.0,(int)std::log10(dx?dx:1)-2.0),
34292  py = (float)std::pow(10.0,(int)std::log10(dy?dy:1)-2.0);
34293  const CImg<Tdouble>
34294  seqx = dx<=0?CImg<Tdouble>::vector(nxmin):CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin+(nxmax-nxmin)*(siz+1)/siz).round(px),
34295  seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin).round(py);
34296 
34297  const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
34298  axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero);
34299  if (nymin>0) axes.draw_axis(seqx,gdimy-1,gray,1,~0U,13,allow_zero);
34300  if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero);
34301  if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero);
34302  if (nxmax<0) axes.draw_axis(gdimx-1,seqy,gray,1,~0U,13,allow_zero);
34303 
34304  cimg_for3x3(axes,x,y,0,0,I,unsigned char)
34305  if (Icc) {
34306  if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
34307  else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
34308  }
34309  else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp) cimg_forC(graph,c) graph(x,y,c) = (graph(x,y,c)+511)/3;
34310 
34311  visu0.draw_image(16,16,graph);
34312  visu0.draw_line(15,15,16+gdimx,15,gray2).draw_line(16+gdimx,15,16+gdimx,16+gdimy,gray2).
34313  draw_line(16+gdimx,16+gdimy,15,16+gdimy,white).draw_line(15,16+gdimy,15,15,white);
34314  } else graph.assign();
34315  text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
34316  visu0.draw_image((visu0.width()-text.width())/2,visu0.height()-14,~text);
34317  text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
34318  visu0.draw_image(1,(visu0.height()-text.height())/2,~text);
34319  visu.assign();
34320  }
34321 
34322  // Generate and display current view.
34323  if (!visu) {
34324  visu.assign(visu0);
34325  if (graph && x0>=0 && x1>=0) {
34326  const int
34327  nx0 = x0<=x1?x0:x1,
34328  nx1 = x0<=x1?x1:x0,
34329  ny0 = y0<=y1?y0:y1,
34330  ny1 = y0<=y1?y1:y0,
34331  sx0 = 16 + nx0*(visu.width()-32)/cimg::max(1U,siz-one),
34332  sx1 = 15 + (nx1+1)*(visu.width()-32)/cimg::max(1U,siz-one),
34333  sy0 = 16 + ny0,
34334  sy1 = 16 + ny1;
34335  if (y0>=0 && y1>=0)
34336  visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
34337  else visu.draw_rectangle(sx0,0,sx1,visu.height()-17,gray,0.5f).
34338  draw_line(sx0,16,sx0,visu.height()-17,black,0.5f,0xCCCCCCCCU).
34339  draw_line(sx1,16,sx1,visu.height()-17,black,0.5f,0xCCCCCCCCU);
34340  }
34341  if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width()-16 && mouse_y<visu.height()-16) {
34342  if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height()-17,black,0.5f,0x55555555U);
34343  const unsigned int x = (unsigned int)cimg::round((mouse_x-16.0f)*(siz-one)/(disp.width()-32),1,one?0:-1);
34344  const double cx = nxmin + x*(nxmax-nxmin)/cimg::max(1U,siz-1);
34345  if (_spectrum>=7)
34346  cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
34347  (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
34348  (double)(*this)(x,0,0,_spectrum-4),(double)(*this)(x,0,0,_spectrum-3),(double)(*this)(x,0,0,_spectrum-1));
34349  else {
34350  cimg_snprintf(message,sizeof(message),"Value[%u:%g] = ( ",x,cx);
34351  cimg_forC(*this,c) std::sprintf(message + std::strlen(message),"%g ",(double)(*this)(x,0,0,c));
34352  std::sprintf(message + std::strlen(message),")");
34353  }
34354  if (x0>=0 && x1>=0) {
34355  const unsigned int
34356  nx0 = x0<=x1?x0:x1,
34357  nx1 = x0<=x1?x1:x0,
34358  ny0 = y0<=y1?y0:y1,
34359  ny1 = y0<=y1?y1:y0;
34360  const double
34361  cx0 = nxmin + nx0*(nxmax-nxmin)/cimg::max(1U,siz-1),
34362  cx1 = nxmin + (nx1+one)*(nxmax-nxmin)/cimg::max(1U,siz-1),
34363  cy0 = nymax - ny0*(nymax-nymin)/(visu._height-32),
34364  cy1 = nymax - ny1*(nymax-nymin)/(visu._height-32);
34365  if (y0>=0 && y1>=0)
34366  std::sprintf(message + std::strlen(message)," - Range ( %u:%g, %g ) - ( %u:%g, %g )",x0,cx0,cy0,x1+one,cx1,cy1);
34367  else
34368  std::sprintf(message + std::strlen(message)," - Range [ %u:%g - %u:%g ]",x0,cx0,x1+one,cx1);
34369  }
34370  text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
34371  visu.draw_image((visu.width()-text.width())/2,1,~text);
34372  }
34373  visu.display(disp);
34374  }
34375 
34376  // Test keys.
34377  switch (okey = key) {
34378 #if cimg_OS!=2
34380 #endif
34381  case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
34382  case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34383  disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
34384  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
34385  _is_resized = true;
34386  disp.set_key(key,false); okey = 0;
34387  } break;
34388  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34389  disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
34390  disp.set_key(key,false); okey = 0;
34391  } break;
34392  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34393  disp.set_fullscreen(false).resize(cimg_fitscreen(640,480,1),false)._is_resized = true;
34394  disp.set_key(key,false); okey = 0;
34395  } break;
34396  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34397  disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
34398  disp.set_key(key,false); okey = 0;
34399  } break;
34400  case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34401  static unsigned int snap_number = 0;
34402  if (visu || visu0) {
34403  CImg<ucharT> &screen = visu?visu:visu0;
34404  char filename[32] = { 0 };
34405  std::FILE *file;
34406  do {
34407  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
34408  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
34409  } while (file);
34410  (+screen).draw_text(0,0," Saving snapshot... ",black,gray,1,13).display(disp);
34411  screen.save(filename);
34412  screen.draw_text(0,0," Snapshot '%s' saved. ",black,gray,1,13,filename).display(disp);
34413  }
34414  disp.set_key(key,false); okey = 0;
34415  } break;
34416  case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
34417  static unsigned int snap_number = 0;
34418  if (visu || visu0) {
34419  CImg<ucharT> &screen = visu?visu:visu0;
34420  char filename[32] = { 0 };
34421  std::FILE *file;
34422  do {
34423 #ifdef cimg_use_zlib
34424  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
34425 #else
34426  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
34427 #endif
34428  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
34429  } while (file);
34430  (+screen).draw_text(0,0," Saving instance... ",black,gray,1,13).display(disp);
34431  save(filename);
34432  screen.draw_text(0,0," Instance '%s' saved. ",black,gray,1,13,filename).display(disp);
34433  }
34434  disp.set_key(key,false); okey = 0;
34435  } break;
34436  }
34437 
34438  // Handle mouse motion and mouse buttons
34439  if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
34440  visu.assign();
34441  if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
34442  const int
34443  mx = (mouse_x -16)*(int)(siz-one)/(disp.width()-32),
34444  cx = mx<0?0:(mx>=(int)(siz-one)?(int)(siz-1-one):mx),
34445  my = mouse_y - 16,
34446  cy = my<=0?0:(my>=(disp.height()-32)?(disp.height()-32):my);
34447  if (button&1) {
34448  if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
34449  }
34450  else if (button&2) {
34451  if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
34452  }
34453  else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
34454  } else if (!button && obutton) selected = true;
34455  obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
34456  }
34457  if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
34458  if (visu && visu0) disp.wait();
34459  }
34460 
34461  disp._normalization = old_normalization;
34462  if (x1>=0 && x1<x0) cimg::swap(x0,x1);
34463  if (y1<y0) cimg::swap(y0,y1);
34464  disp.set_key(okey);
34465  return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1+(int)one:-1,y1);
34466  }
34467 
34469 
34474  CImg<T>& load(const char *const filename) {
34475  if (!filename)
34476  throw CImgArgumentException(_cimg_instance
34477  "load(): Specified filename is (null).",
34478  cimg_instance);
34479 
34480  if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
34481  char filename_local[1024] = { 0 };
34482  load(cimg::load_network_external(filename,filename_local));
34483  std::remove(filename_local);
34484  return *this;
34485  }
34486 
34487  const char *const ext = cimg::split_filename(filename);
34488  const unsigned int omode = cimg::exception_mode();
34489  cimg::exception_mode() = 0;
34490  try {
34491 #ifdef cimg_load_plugin
34492  cimg_load_plugin(filename);
34493 #endif
34494 #ifdef cimg_load_plugin1
34495  cimg_load_plugin1(filename);
34496 #endif
34497 #ifdef cimg_load_plugin2
34498  cimg_load_plugin2(filename);
34499 #endif
34500 #ifdef cimg_load_plugin3
34501  cimg_load_plugin3(filename);
34502 #endif
34503 #ifdef cimg_load_plugin4
34504  cimg_load_plugin4(filename);
34505 #endif
34506 #ifdef cimg_load_plugin5
34507  cimg_load_plugin5(filename);
34508 #endif
34509 #ifdef cimg_load_plugin6
34510  cimg_load_plugin6(filename);
34511 #endif
34512 #ifdef cimg_load_plugin7
34513  cimg_load_plugin7(filename);
34514 #endif
34515 #ifdef cimg_load_plugin8
34516  cimg_load_plugin8(filename);
34517 #endif
34518  // Ascii formats
34519  if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
34520  else if (!cimg::strcasecmp(ext,"dlm") ||
34521  !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
34522 
34523  // 2d binary formats
34524  else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
34525  else if (!cimg::strcasecmp(ext,"jpg") ||
34526  !cimg::strcasecmp(ext,"jpeg") ||
34527  !cimg::strcasecmp(ext,"jpe") ||
34528  !cimg::strcasecmp(ext,"jfif") ||
34529  !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
34530  else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
34531  else if (!cimg::strcasecmp(ext,"ppm") ||
34532  !cimg::strcasecmp(ext,"pgm") ||
34533  !cimg::strcasecmp(ext,"pnm") ||
34534  !cimg::strcasecmp(ext,"pbm") ||
34535  !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
34536  else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
34537  else if (!cimg::strcasecmp(ext,"tif") ||
34538  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
34539  else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
34540  else if (!cimg::strcasecmp(ext,"cr2") ||
34541  !cimg::strcasecmp(ext,"crw") ||
34542  !cimg::strcasecmp(ext,"dcr") ||
34543  !cimg::strcasecmp(ext,"mrw") ||
34544  !cimg::strcasecmp(ext,"nef") ||
34545  !cimg::strcasecmp(ext,"orf") ||
34546  !cimg::strcasecmp(ext,"pix") ||
34547  !cimg::strcasecmp(ext,"ptx") ||
34548  !cimg::strcasecmp(ext,"raf") ||
34549  !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
34550 
34551  // 3d binary formats
34552  else if (!cimg::strcasecmp(ext,"dcm") ||
34553  !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
34554  else if (!cimg::strcasecmp(ext,"hdr") ||
34555  !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
34556  else if (!cimg::strcasecmp(ext,"par") ||
34557  !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
34558  else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
34559  else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
34560  else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
34561  else if (!cimg::strcasecmp(ext,"cimg") ||
34562  !cimg::strcasecmp(ext,"cimgz") ||
34563  !*ext) return load_cimg(filename);
34564 
34565  // Archive files
34566  else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
34567 
34568  // Image sequences
34569  else if (!cimg::strcasecmp(ext,"avi") ||
34570  !cimg::strcasecmp(ext,"mov") ||
34571  !cimg::strcasecmp(ext,"asf") ||
34572  !cimg::strcasecmp(ext,"divx") ||
34573  !cimg::strcasecmp(ext,"flv") ||
34574  !cimg::strcasecmp(ext,"mpg") ||
34575  !cimg::strcasecmp(ext,"m1v") ||
34576  !cimg::strcasecmp(ext,"m2v") ||
34577  !cimg::strcasecmp(ext,"m4v") ||
34578  !cimg::strcasecmp(ext,"mjp") ||
34579  !cimg::strcasecmp(ext,"mkv") ||
34580  !cimg::strcasecmp(ext,"mpe") ||
34581  !cimg::strcasecmp(ext,"movie") ||
34582  !cimg::strcasecmp(ext,"ogm") ||
34583  !cimg::strcasecmp(ext,"ogg") ||
34584  !cimg::strcasecmp(ext,"qt") ||
34585  !cimg::strcasecmp(ext,"rm") ||
34586  !cimg::strcasecmp(ext,"vob") ||
34587  !cimg::strcasecmp(ext,"wmv") ||
34588  !cimg::strcasecmp(ext,"xvid") ||
34589  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
34590  else throw CImgIOException("CImg<%s>::load()",
34591  pixel_type());
34592  } catch (CImgIOException&) {
34593  std::FILE *file = 0;
34594  try {
34595  file = cimg::fopen(filename,"rb");
34596  } catch (CImgIOException&) {
34597  cimg::exception_mode() = omode;
34598  throw CImgIOException(_cimg_instance
34599  "load(): Failed to open file '%s'.",
34600  cimg_instance,
34601  filename);
34602  }
34603  try {
34604  const char *const f_type = cimg::file_type(file,filename);
34605  std::fclose(file);
34606  if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
34607  else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
34608  else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
34609  else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
34610  else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
34611  else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
34612  else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
34613  else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
34614  else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
34615  else throw CImgIOException("CImg<%s>::load()",
34616  pixel_type());
34617  } catch (CImgIOException&) {
34618  try {
34619  load_other(filename);
34620  } catch (CImgIOException&) {
34621  cimg::exception_mode() = omode;
34622  throw CImgIOException(_cimg_instance
34623  "load(): Failed to recognize format of file '%s'.",
34624  cimg_instance,
34625  filename);
34626  }
34627  }
34628  }
34629  cimg::exception_mode() = omode;
34630  return *this;
34631  }
34632 
34634  static CImg<T> get_load(const char *const filename) {
34635  return CImg<T>().load(filename);
34636  }
34637 
34639 
34642  CImg<T>& load_ascii(const char *const filename) {
34643  return _load_ascii(0,filename);
34644  }
34645 
34647  static CImg<T> get_load_ascii(const char *const filename) {
34648  return CImg<T>().load_ascii(filename);
34649  }
34650 
34652  CImg<T>& load_ascii(std::FILE *const file) {
34653  return _load_ascii(file,0);
34654  }
34655 
34657  static CImg<T> get_load_ascii(std::FILE *const file) {
34658  return CImg<T>().load_ascii(file);
34659  }
34660 
34661  CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
34662  if (!file && !filename)
34663  throw CImgArgumentException(_cimg_instance
34664  "load_ascii(): Specified filename is (null).",
34665  cimg_instance);
34666 
34667  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
34668  char line[256] = { 0 };
34669  int err = std::fscanf(nfile,"%255[^\n]",line);
34670  unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
34671  std::sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
34672  err = std::fscanf(nfile,"%*[^0-9.eE+-]");
34673  if (!dx || !dy || !dz || !dc) {
34674  if (!file) cimg::fclose(nfile);
34675  throw CImgIOException(_cimg_instance
34676  "load_ascii(): Invalid ascii header in file '%s', image dimensions are set to (%u,%u,%u,%u).",
34677  cimg_instance,
34678  filename?filename:"(FILE*)",dx,dy,dz,dc);
34679  }
34680  assign(dx,dy,dz,dc);
34681  const unsigned long siz = size();
34682  unsigned long off = 0;
34683  double val;
34684  T *ptr = _data;
34685  for (err = 1, off = 0; off<siz && err==1; ++off) {
34686  err = std::fscanf(nfile,"%lf%*[^0-9.eE+-]",&val);
34687  *(ptr++) = (T)val;
34688  }
34689  if (err!=1)
34690  cimg::warn(_cimg_instance
34691  "load_ascii(): Only %lu/%lu values read from file '%s'.",
34692  cimg_instance,
34693  off-1,siz,filename?filename:"(FILE*)");
34694 
34695  if (!file) cimg::fclose(nfile);
34696  return *this;
34697  }
34698 
34700 
34703  CImg<T>& load_dlm(const char *const filename) {
34704  return _load_dlm(0,filename);
34705  }
34706 
34708  static CImg<T> get_load_dlm(const char *const filename) {
34709  return CImg<T>().load_dlm(filename);
34710  }
34711 
34713  CImg<T>& load_dlm(std::FILE *const file) {
34714  return _load_dlm(file,0);
34715  }
34716 
34718  static CImg<T> get_load_dlm(std::FILE *const file) {
34719  return CImg<T>().load_dlm(file);
34720  }
34721 
34722  CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
34723  if (!file && !filename)
34724  throw CImgArgumentException(_cimg_instance
34725  "load_dlm(): Specified filename is (null).",
34726  cimg_instance);
34727 
34728  std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
34729  char delimiter[256] = { 0 }, tmp[256] = { 0 };
34730  unsigned int cdx = 0, dx = 0, dy = 0;
34731  int err = 0;
34732  double val;
34733  assign(256,256);
34734  while ((err = std::fscanf(nfile,"%lf%255[^0-9.+-]",&val,delimiter))>0) {
34735  if (err>0) (*this)(cdx++,dy) = (T)val;
34736  if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
34737  char c = 0;
34738  if (!std::sscanf(delimiter,"%255[^\n]%c",tmp,&c) || c=='\n') {
34739  dx = cimg::max(cdx,dx);
34740  if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
34741  cdx = 0;
34742  }
34743  }
34744  if (cdx && err==1) { dx = cdx; ++dy; }
34745  if (!dx || !dy) {
34746  if (!file) cimg::fclose(nfile);
34747  throw CImgIOException(_cimg_instance
34748  "load_dlm(): Invalid DLM file '%s'.",
34749  cimg_instance,
34750  filename?filename:"(FILE*)");
34751  }
34752  resize(dx,dy,1,1,0);
34753  if (!file) cimg::fclose(nfile);
34754  return *this;
34755  }
34756 
34758 
34761  CImg<T>& load_bmp(const char *const filename) {
34762  return _load_bmp(0,filename);
34763  }
34764 
34766  static CImg<T> get_load_bmp(const char *const filename) {
34767  return CImg<T>().load_bmp(filename);
34768  }
34769 
34771  CImg<T>& load_bmp(std::FILE *const file) {
34772  return _load_bmp(file,0);
34773  }
34774 
34776  static CImg<T> get_load_bmp(std::FILE *const file) {
34777  return CImg<T>().load_bmp(file);
34778  }
34779 
34780  CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
34781  if (!file && !filename)
34782  throw CImgArgumentException(_cimg_instance
34783  "load_bmp(): Specified filename is (null).",
34784  cimg_instance);
34785 
34786  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
34787  unsigned char header[64] = { 0 };
34788  cimg::fread(header,54,nfile);
34789  if (*header!='B' || header[1]!='M') {
34790  if (!file) cimg::fclose(nfile);
34791  throw CImgIOException(_cimg_instance
34792  "load_bmp(): Invalid BMP file '%s'.",
34793  cimg_instance,
34794  filename?filename:"(FILE*)");
34795  }
34796 
34797  // Read header and pixel buffer
34798  int
34799  file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
34800  offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
34801  dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
34802  dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
34803  compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
34804  nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
34805  bpp = header[0x1C] + (header[0x1D]<<8);
34806 
34807  if (!file_size || file_size==offset) {
34808  std::fseek(nfile,0,SEEK_END);
34809  file_size = (int)std::ftell(nfile);
34810  std::fseek(nfile,54,SEEK_SET);
34811  }
34812 
34813  const int
34814  cimg_iobuffer = 12*1024*1024,
34815  dx_bytes = (bpp==1)?(dx/8+(dx%8?1:0)):((bpp==4)?(dx/2+(dx%2?1:0)):(dx*bpp/8)),
34816  align_bytes = (4-dx_bytes%4)%4,
34817  buf_size = cimg::min(cimg::abs(dy)*(dx_bytes + align_bytes),file_size - offset);
34818 
34819  CImg<intT> colormap;
34820  if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
34821  if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
34822  const int xoffset = offset - 54 - 4*nb_colors;
34823  if (xoffset>0) std::fseek(nfile,xoffset,SEEK_CUR);
34824 
34825  CImg<ucharT> buffer;
34826  if (buf_size<cimg_iobuffer) { buffer.assign(buf_size); cimg::fread(buffer._data,buf_size,nfile); }
34827  else buffer.assign(dx_bytes + align_bytes);
34828  unsigned char *ptrs = buffer;
34829 
34830  // Decompress buffer (if necessary)
34831  if (compression) {
34832  if (file)
34833  throw CImgIOException(_cimg_instance
34834  "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
34835  cimg_instance);
34836  else {
34837  if (!file) cimg::fclose(nfile);
34838  return load_other(filename);
34839  }
34840  }
34841 
34842  // Read pixel data
34843  assign(dx,cimg::abs(dy),1,3);
34844  switch (bpp) {
34845  case 1 : { // Monochrome
34846  for (int y = height()-1; y>=0; --y) {
34847  if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34848  unsigned char mask = 0x80, val = 0;
34849  cimg_forX(*this,x) {
34850  if (mask==0x80) val = *(ptrs++);
34851  const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
34852  (*this)(x,y,2) = (T)*(col++);
34853  (*this)(x,y,1) = (T)*(col++);
34854  (*this)(x,y,0) = (T)*(col++);
34855  mask = cimg::ror(mask);
34856  }
34857  ptrs+=align_bytes;
34858  }
34859  } break;
34860  case 4 : { // 16 colors
34861  for (int y = height()-1; y>=0; --y) {
34862  if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34863  unsigned char mask = 0xF0, val = 0;
34864  cimg_forX(*this,x) {
34865  if (mask==0xF0) val = *(ptrs++);
34866  const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
34867  const unsigned char *col = (unsigned char*)(colormap._data + color);
34868  (*this)(x,y,2) = (T)*(col++);
34869  (*this)(x,y,1) = (T)*(col++);
34870  (*this)(x,y,0) = (T)*(col++);
34871  mask = cimg::ror(mask,4);
34872  }
34873  ptrs+=align_bytes;
34874  }
34875  } break;
34876  case 8 : { // 256 colors
34877  for (int y = height()-1; y>=0; --y) {
34878  if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34879  cimg_forX(*this,x) {
34880  const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
34881  (*this)(x,y,2) = (T)*(col++);
34882  (*this)(x,y,1) = (T)*(col++);
34883  (*this)(x,y,0) = (T)*(col++);
34884  }
34885  ptrs+=align_bytes;
34886  }
34887  } break;
34888  case 16 : { // 16 bits colors
34889  for (int y = height()-1; y>=0; --y) {
34890  if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34891  cimg_forX(*this,x) {
34892  const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
34893  const unsigned short col = (unsigned short)(c1|(c2<<8));
34894  (*this)(x,y,2) = (T)(col&0x1F);
34895  (*this)(x,y,1) = (T)((col>>5)&0x1F);
34896  (*this)(x,y,0) = (T)((col>>10)&0x1F);
34897  }
34898  ptrs+=align_bytes;
34899  }
34900  } break;
34901  case 24 : { // 24 bits colors
34902  for (int y = height()-1; y>=0; --y) {
34903  if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34904  cimg_forX(*this,x) {
34905  (*this)(x,y,2) = (T)*(ptrs++);
34906  (*this)(x,y,1) = (T)*(ptrs++);
34907  (*this)(x,y,0) = (T)*(ptrs++);
34908  }
34909  ptrs+=align_bytes;
34910  }
34911  } break;
34912  case 32 : { // 32 bits colors
34913  for (int y = height()-1; y>=0; --y) {
34914  if (buf_size>=cimg_iobuffer) { cimg::fread(ptrs=buffer._data,dx_bytes,nfile); std::fseek(nfile,align_bytes,SEEK_CUR); }
34915  cimg_forX(*this,x) {
34916  (*this)(x,y,2) = (T)*(ptrs++);
34917  (*this)(x,y,1) = (T)*(ptrs++);
34918  (*this)(x,y,0) = (T)*(ptrs++);
34919  ++ptrs;
34920  }
34921  ptrs+=align_bytes;
34922  }
34923  } break;
34924  }
34925  if (dy<0) mirror('y');
34926  if (!file) cimg::fclose(nfile);
34927  return *this;
34928  }
34929 
34931 
34934  CImg<T>& load_jpeg(const char *const filename) {
34935  return _load_jpeg(0,filename);
34936  }
34937 
34939  static CImg<T> get_load_jpeg(const char *const filename) {
34940  return CImg<T>().load_jpeg(filename);
34941  }
34942 
34944  CImg<T>& load_jpeg(std::FILE *const file) {
34945  return _load_jpeg(file,0);
34946  }
34947 
34949  static CImg<T> get_load_jpeg(std::FILE *const file) {
34950  return CImg<T>().load_jpeg(file);
34951  }
34952 
34953  // Custom error handler for libjpeg.
34954 #ifdef cimg_use_jpeg
34955  struct _cimg_error_mgr {
34956  struct jpeg_error_mgr original;
34957  jmp_buf setjmp_buffer;
34958  char message[JMSG_LENGTH_MAX];
34959  };
34960 
34961  typedef struct _cimg_error_mgr *_cimg_error_ptr;
34962 
34963  METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
34964  _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point
34965  (*cinfo->err->format_message)(cinfo,c_err->message);
34966  jpeg_destroy(cinfo); // Clean memory and temp files.
34967  longjmp(c_err->setjmp_buffer,1);
34968  }
34969 #endif
34970 
34971  CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
34972  if (!file && !filename)
34973  throw CImgArgumentException(_cimg_instance
34974  "load_jpeg(): Specified filename is (null).",
34975  cimg_instance);
34976 
34977 #ifndef cimg_use_jpeg
34978  if (file)
34979  throw CImgIOException(_cimg_instance
34980  "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
34981  cimg_instance);
34982  else return load_other(filename);
34983 #else
34984 
34985  struct jpeg_decompress_struct cinfo;
34986  struct _cimg_error_mgr jerr;
34987  cinfo.err = jpeg_std_error(&jerr.original);
34988  jerr.original.error_exit = _cimg_jpeg_error_exit;
34989 
34990  if (setjmp(jerr.setjmp_buffer)) { // JPEG error
34991  throw CImgIOException(_cimg_instance
34992  "load_jpeg(): Error message returned by libjpeg: %s.",
34993  cimg_instance,jerr.message);
34994  }
34995 
34996  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
34997  jpeg_create_decompress(&cinfo);
34998  jpeg_stdio_src(&cinfo,nfile);
34999  jpeg_read_header(&cinfo,TRUE);
35000  jpeg_start_decompress(&cinfo);
35001 
35002  if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
35003  if (!file) return load_other(filename);
35004  else
35005  throw CImgIOException(_cimg_instance
35006  "load_jpeg(): Failed to load JPEG data from file '%s'.",
35007  cimg_instance,filename?filename:"(FILE*)");
35008  }
35009  CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
35010  JSAMPROW row_pointer[1];
35011  assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components);
35012  T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height, *ptr_a = _data + 3UL*_width*_height;
35013  while (cinfo.output_scanline<cinfo.output_height) {
35014  *row_pointer = buffer._data;
35015  if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
35016  cimg::warn(_cimg_instance
35017  "load_jpeg(): Incomplete data in file '%s'.",
35018  cimg_instance,filename?filename:"(FILE*)");
35019  break;
35020  }
35021  const unsigned char *ptrs = buffer._data;
35022  switch (_spectrum) {
35023  case 1 : {
35024  cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
35025  } break;
35026  case 3 : {
35027  cimg_forX(*this,x) {
35028  *(ptr_r++) = (T)*(ptrs++);
35029  *(ptr_g++) = (T)*(ptrs++);
35030  *(ptr_b++) = (T)*(ptrs++);
35031  }
35032  } break;
35033  case 4 : {
35034  cimg_forX(*this,x) {
35035  *(ptr_r++) = (T)*(ptrs++);
35036  *(ptr_g++) = (T)*(ptrs++);
35037  *(ptr_b++) = (T)*(ptrs++);
35038  *(ptr_a++) = (T)*(ptrs++);
35039  }
35040  } break;
35041  }
35042  }
35043  jpeg_finish_decompress(&cinfo);
35044  jpeg_destroy_decompress(&cinfo);
35045  if (!file) cimg::fclose(nfile);
35046  return *this;
35047 #endif
35048  }
35049 
35051 
35054  // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
35055  // This is experimental code, not much tested, use with care.
35056  CImg<T>& load_magick(const char *const filename) {
35057  if (!filename)
35058  throw CImgArgumentException(_cimg_instance
35059  "load_magick(): Specified filename is (null).",
35060  cimg_instance);
35061 
35062 #ifdef cimg_use_magick
35063  Magick::Image image(filename);
35064  const unsigned int W = image.size().width(), H = image.size().height();
35065  switch (image.type()) {
35066  case Magick::PaletteMatteType :
35067  case Magick::TrueColorMatteType :
35068  case Magick::ColorSeparationType : {
35069  assign(W,H,1,4);
35070  T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
35071  Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
35072  for (unsigned long off = (unsigned long)W*H; off; --off) {
35073  *(ptr_r++) = (T)(pixels->red);
35074  *(ptr_g++) = (T)(pixels->green);
35075  *(ptr_b++) = (T)(pixels->blue);
35076  *(ptr_a++) = (T)(pixels->opacity);
35077  ++pixels;
35078  }
35079  } break;
35080  case Magick::PaletteType :
35081  case Magick::TrueColorType : {
35082  assign(W,H,1,3);
35083  T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
35084  Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
35085  for (unsigned long off = (unsigned long)W*H; off; --off) {
35086  *(ptr_r++) = (T)(pixels->red);
35087  *(ptr_g++) = (T)(pixels->green);
35088  *(ptr_b++) = (T)(pixels->blue);
35089  ++pixels;
35090  }
35091  } break;
35092  case Magick::GrayscaleMatteType : {
35093  assign(W,H,1,2);
35094  T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
35095  Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
35096  for (unsigned long off = (unsigned long)W*H; off; --off) {
35097  *(ptr_r++) = (T)(pixels->red);
35098  *(ptr_a++) = (T)(pixels->opacity);
35099  ++pixels;
35100  }
35101  } break;
35102  default : {
35103  assign(W,H,1,1);
35104  T *ptr_r = data(0,0,0,0);
35105  Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
35106  for (unsigned long off = (unsigned long)W*H; off; --off) {
35107  *(ptr_r++) = (T)(pixels->red);
35108  ++pixels;
35109  }
35110  }
35111  }
35112 #else
35113  throw CImgIOException(_cimg_instance
35114  "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
35115  cimg_instance,
35116  filename);
35117 #endif
35118  return *this;
35119  }
35120 
35122  static CImg<T> get_load_magick(const char *const filename) {
35123  return CImg<T>().load_magick(filename);
35124  }
35125 
35127 
35130  CImg<T>& load_png(const char *const filename) {
35131  return _load_png(0,filename);
35132  }
35133 
35135  static CImg<T> get_load_png(const char *const filename) {
35136  return CImg<T>().load_png(filename);
35137  }
35138 
35140  CImg<T>& load_png(std::FILE *const file) {
35141  return _load_png(file,0);
35142  }
35143 
35145  static CImg<T> get_load_png(std::FILE *const file) {
35146  return CImg<T>().load_png(file);
35147  }
35148 
35149  // (Note: Most of this function has been written by Eric Fausett)
35150  CImg<T>& _load_png(std::FILE *const file, const char *const filename) {
35151  if (!file && !filename)
35152  throw CImgArgumentException(_cimg_instance
35153  "load_png(): Specified filename is (null).",
35154  cimg_instance);
35155 
35156 #ifndef cimg_use_png
35157  if (file)
35158  throw CImgIOException(_cimg_instance
35159  "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
35160  cimg_instance);
35161 
35162  else return load_other(filename);
35163 #else
35164  // Open file and check for PNG validity
35165  const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
35166  std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
35167 
35168  unsigned char pngCheck[8] = { 0 };
35169  cimg::fread(pngCheck,8,(std::FILE*)nfile);
35170  if (png_sig_cmp(pngCheck,0,8)) {
35171  if (!file) cimg::fclose(nfile);
35172  throw CImgIOException(_cimg_instance
35173  "load_png(): Invalid PNG file '%s'.",
35174  cimg_instance,
35175  nfilename?nfilename:"(FILE*)");
35176  }
35177 
35178  // Setup PNG structures for read
35179  png_voidp user_error_ptr = 0;
35180  png_error_ptr user_error_fn = 0, user_warning_fn = 0;
35181  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
35182  if (!png_ptr) {
35183  if (!file) cimg::fclose(nfile);
35184  throw CImgIOException(_cimg_instance
35185  "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
35186  cimg_instance,
35187  nfilename?nfilename:"(FILE*)");
35188  }
35189  png_infop info_ptr = png_create_info_struct(png_ptr);
35190  if (!info_ptr) {
35191  if (!file) cimg::fclose(nfile);
35192  png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
35193  throw CImgIOException(_cimg_instance
35194  "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
35195  cimg_instance,
35196  nfilename?nfilename:"(FILE*)");
35197  }
35198  png_infop end_info = png_create_info_struct(png_ptr);
35199  if (!end_info) {
35200  if (!file) cimg::fclose(nfile);
35201  png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
35202  throw CImgIOException(_cimg_instance
35203  "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
35204  cimg_instance,
35205  nfilename?nfilename:"(FILE*)");
35206  }
35207 
35208  // Error handling callback for png file reading
35209  if (setjmp(png_jmpbuf(png_ptr))) {
35210  if (!file) cimg::fclose((std::FILE*)nfile);
35211  png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
35212  throw CImgIOException(_cimg_instance
35213  "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
35214  cimg_instance,
35215  nfilename?nfilename:"(FILE*)");
35216  }
35217  png_init_io(png_ptr, nfile);
35218  png_set_sig_bytes(png_ptr, 8);
35219 
35220  // Get PNG Header Info up to data block
35221  png_read_info(png_ptr,info_ptr);
35222  png_uint_32 W, H;
35223  int bit_depth, color_type, interlace_type;
35224  bool is_gray = false;
35225  png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
35226 
35227  // Transforms to unify image data
35228  if (color_type==PNG_COLOR_TYPE_PALETTE) {
35229  png_set_palette_to_rgb(png_ptr);
35230  color_type = PNG_COLOR_TYPE_RGB;
35231  bit_depth = 8;
35232  }
35233  if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
35234  png_set_expand_gray_1_2_4_to_8(png_ptr);
35235  is_gray = true;
35236  bit_depth = 8;
35237  }
35238  if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
35239  png_set_tRNS_to_alpha(png_ptr);
35240  color_type |= PNG_COLOR_MASK_ALPHA;
35241  }
35242  if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
35243  png_set_gray_to_rgb(png_ptr);
35244  color_type |= PNG_COLOR_MASK_COLOR;
35245  is_gray = true;
35246  }
35247  if (color_type==PNG_COLOR_TYPE_RGB)
35248  png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
35249 
35250  png_read_update_info(png_ptr,info_ptr);
35251  if (bit_depth!=8 && bit_depth!=16) {
35252  if (!file) cimg::fclose(nfile);
35253  png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
35254  throw CImgIOException(_cimg_instance
35255  "load_png(): Invalid bit depth %u in file '%s'.",
35256  cimg_instance,
35257  bit_depth,nfilename?nfilename:"(FILE*)");
35258  }
35259  const int byte_depth = bit_depth>>3;
35260 
35261  // Allocate Memory for Image Read
35262  png_bytep *const imgData = new png_bytep[H];
35263  for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[byte_depth*4*W];
35264  png_read_image(png_ptr,imgData);
35265  png_read_end(png_ptr,end_info);
35266 
35267  // Read pixel data
35268  if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
35269  if (!file) cimg::fclose(nfile);
35270  png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
35271  throw CImgIOException(_cimg_instance
35272  "load_png(): Invalid color coding type %u in file '%s'.",
35273  cimg_instance,
35274  color_type,nfilename?nfilename:"(FILE*)");
35275  }
35276  const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
35277  assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0));
35278  T
35279  *ptr_r = data(0,0,0,0),
35280  *ptr_g = is_gray?0:data(0,0,0,1),
35281  *ptr_b = is_gray?0:data(0,0,0,2),
35282  *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
35283  switch (bit_depth) {
35284  case 8 : {
35285  cimg_forY(*this,y) {
35286  const unsigned char *ptrs = (unsigned char*)imgData[y];
35287  cimg_forX(*this,x) {
35288  *(ptr_r++) = (T)*(ptrs++);
35289  if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
35290  if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
35291  if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
35292  }
35293  }
35294  } break;
35295  case 16 : {
35296  cimg_forY(*this,y) {
35297  const unsigned short *ptrs = (unsigned short*)(imgData[y]);
35298  if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
35299  cimg_forX(*this,x) {
35300  *(ptr_r++) = (T)*(ptrs++);
35301  if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
35302  if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
35303  if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
35304  }
35305  }
35306  } break;
35307  }
35308  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
35309 
35310  // Deallocate Image Read Memory
35311  cimg_forY(*this,n) delete[] imgData[n];
35312  delete[] imgData;
35313  if (!file) cimg::fclose(nfile);
35314  return *this;
35315 #endif
35316  }
35317 
35319 
35322  CImg<T>& load_pnm(const char *const filename) {
35323  return _load_pnm(0,filename);
35324  }
35325 
35327  static CImg<T> get_load_pnm(const char *const filename) {
35328  return CImg<T>().load_pnm(filename);
35329  }
35330 
35332  CImg<T>& load_pnm(std::FILE *const file) {
35333  return _load_pnm(file,0);
35334  }
35335 
35337  static CImg<T> get_load_pnm(std::FILE *const file) {
35338  return CImg<T>().load_pnm(file);
35339  }
35340 
35341  CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
35342  if (!file && !filename)
35343  throw CImgArgumentException(_cimg_instance
35344  "load_pnm(): Specified filename is (null).",
35345  cimg_instance);
35346 
35347  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35348  unsigned int ppm_type, W, H, D = 1, colormax = 255;
35349  char item[1024] = { 0 };
35350  int err, rval, gval, bval;
35351  const long cimg_iobuffer = 12*1024*1024;
35352  while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35353  if (std::sscanf(item," P%u",&ppm_type)!=1) {
35354  if (!file) cimg::fclose(nfile);
35355  throw CImgIOException(_cimg_instance
35356  "load_pnm(): PNM header not found in file '%s'.",
35357  cimg_instance,
35358  filename?filename:"(FILE*)");
35359  }
35360  while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35361  if ((err=std::sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
35362  if (!file) cimg::fclose(nfile);
35363  throw CImgIOException(_cimg_instance
35364  "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
35365  cimg_instance,
35366  filename?filename:"(FILE*)");
35367  }
35368  if (ppm_type!=1 && ppm_type!=4) {
35369  if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
35370  while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35371  if (std::sscanf(item,"%u",&colormax)!=1)
35372  cimg::warn(_cimg_instance
35373  "load_pnm(): COLORMAX field is undefined in file '%s'.",
35374  cimg_instance,
35375  filename?filename:"(FILE*)");
35376  } else { colormax = D; D = 1; }
35377  }
35378  std::fgetc(nfile);
35379 
35380  switch (ppm_type) {
35381  case 1 : { // 2d b&w ascii.
35382  assign(W,H,1,1);
35383  T* ptrd = _data;
35384  cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
35385  } break;
35386  case 2 : { // 2d grey ascii.
35387  assign(W,H,1,1);
35388  T* ptrd = _data;
35389  cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
35390  } break;
35391  case 3 : { // 2d color ascii.
35392  assign(W,H,1,3);
35393  T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
35394  cimg_forXY(*this,x,y) {
35395  if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) { *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval; }
35396  else break;
35397  }
35398  } break;
35399  case 4 : { // 2d b&w binary (support 3D PINK extension).
35400  CImg<ucharT> raw;
35401  assign(W,H,D,1);
35402  T *ptrd = data(0,0,0,0);
35403  unsigned int w = 0, h = 0, d = 0;
35404  for (long to_read = (long)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
35405  raw.assign(cimg::min(to_read,cimg_iobuffer));
35406  cimg::fread(raw._data,raw._width,nfile);
35407  to_read-=raw._width;
35408  const unsigned char *ptrs = raw._data;
35409  unsigned char mask = 0, val = 0;
35410  for (unsigned long off = (unsigned long)raw._width; off || mask; mask>>=1) {
35411  if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
35412  *(ptrd++) = (T)((val&mask)?0:255);
35413  if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
35414  }
35415  }
35416  } break;
35417  case 5 : case 7 : { // 2d/3d grey binary (support 3D PINK extension).
35418  if (colormax<256) { // 8 bits.
35419  CImg<ucharT> raw;
35420  assign(W,H,D,1);
35421  T *ptrd = data(0,0,0,0);
35422  for (long to_read = (long)size(); to_read>0; ) {
35423  raw.assign(cimg::min(to_read,cimg_iobuffer));
35424  cimg::fread(raw._data,raw._width,nfile);
35425  to_read-=raw._width;
35426  const unsigned char *ptrs = raw._data;
35427  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
35428  }
35429  } else { // 16 bits.
35430  CImg<ushortT> raw;
35431  assign(W,H,D,1);
35432  T *ptrd = data(0,0,0,0);
35433  for (long to_read = (long)size(); to_read>0; ) {
35434  raw.assign(cimg::min(to_read,cimg_iobuffer/2));
35435  cimg::fread(raw._data,raw._width,nfile);
35436  if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
35437  to_read-=raw._width;
35438  const unsigned short *ptrs = raw._data;
35439  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
35440  }
35441  }
35442  } break;
35443  case 6 : { // 2d color binary.
35444  if (colormax<256) { // 8 bits.
35445  CImg<ucharT> raw;
35446  assign(W,H,1,3);
35447  T
35448  *ptr_r = data(0,0,0,0),
35449  *ptr_g = data(0,0,0,1),
35450  *ptr_b = data(0,0,0,2);
35451  for (long to_read = (long)size(); to_read>0; ) {
35452  raw.assign(cimg::min(to_read,cimg_iobuffer));
35453  cimg::fread(raw._data,raw._width,nfile);
35454  to_read-=raw._width;
35455  const unsigned char *ptrs = raw._data;
35456  for (unsigned long off = (unsigned long)raw._width/3; off; --off) {
35457  *(ptr_r++) = (T)*(ptrs++);
35458  *(ptr_g++) = (T)*(ptrs++);
35459  *(ptr_b++) = (T)*(ptrs++);
35460  }
35461  }
35462  } else { // 16 bits.
35463  CImg<ushortT> raw;
35464  assign(W,H,1,3);
35465  T
35466  *ptr_r = data(0,0,0,0),
35467  *ptr_g = data(0,0,0,1),
35468  *ptr_b = data(0,0,0,2);
35469  for (long to_read = (int)size(); to_read>0; ) {
35470  raw.assign(cimg::min(to_read,cimg_iobuffer/2));
35471  cimg::fread(raw._data,raw._width,nfile);
35472  if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
35473  to_read-=raw._width;
35474  const unsigned short *ptrs = raw._data;
35475  for (unsigned long off = (unsigned long)raw._width/3; off; --off) {
35476  *(ptr_r++) = (T)*(ptrs++);
35477  *(ptr_g++) = (T)*(ptrs++);
35478  *(ptr_b++) = (T)*(ptrs++);
35479  }
35480  }
35481  }
35482  } break;
35483  case 8 : { // 2d/3d grey binary with int32 integers (PINK extension).
35484  CImg<intT> raw;
35485  assign(W,H,D,1);
35486  T *ptrd = data(0,0,0,0);
35487  for (long to_read = (long)size(); to_read>0; ) {
35488  raw.assign(cimg::min(to_read,cimg_iobuffer));
35489  cimg::fread(raw._data,raw._width,nfile);
35490  to_read-=raw._width;
35491  const int *ptrs = raw._data;
35492  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
35493  }
35494  } break;
35495  case 9 : { // 2d/3d grey binary with float values (PINK extension).
35496  CImg<floatT> raw;
35497  assign(W,H,D,1);
35498  T *ptrd = data(0,0,0,0);
35499  for (long to_read = (long)size(); to_read>0; ) {
35500  raw.assign(cimg::min(to_read,cimg_iobuffer));
35501  cimg::fread(raw._data,raw._width,nfile);
35502  to_read-=raw._width;
35503  const float *ptrs = raw._data;
35504  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
35505  }
35506  } break;
35507  default :
35508  assign();
35509  if (!file) cimg::fclose(nfile);
35510  throw CImgIOException(_cimg_instance
35511  "load_pnm(): PNM type 'P%d' found, but type is not supported.",
35512  cimg_instance,
35513  filename?filename:"(FILE*)",ppm_type);
35514  }
35515  if (!file) cimg::fclose(nfile);
35516  return *this;
35517  }
35518 
35520 
35523  CImg<T>& load_pfm(const char *const filename) {
35524  return _load_pfm(0,filename);
35525  }
35526 
35528  static CImg<T> get_load_pfm(const char *const filename) {
35529  return CImg<T>().load_pfm(filename);
35530  }
35531 
35533  CImg<T>& load_pfm(std::FILE *const file) {
35534  return _load_pfm(file,0);
35535  }
35536 
35538  static CImg<T> get_load_pfm(std::FILE *const file) {
35539  return CImg<T>().load_pfm(file);
35540  }
35541 
35542  CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
35543  if (!file && !filename)
35544  throw CImgArgumentException(_cimg_instance
35545  "load_pfm(): Specified filename is (null).",
35546  cimg_instance);
35547 
35548  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35549  char pfm_type, item[1024] = { 0 };
35550  int W = 0, H = 0, err = 0;
35551  double scale = 0;
35552  while ((err=std::fscanf(nfile,"%1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35553  if (std::sscanf(item," P%c",&pfm_type)!=1) {
35554  if (!file) cimg::fclose(nfile);
35555  throw CImgIOException(_cimg_instance
35556  "load_pfm(): PFM header not found in file '%s'.",
35557  cimg_instance,
35558  filename?filename:"(FILE*)");
35559  }
35560  while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35561  if ((err=std::sscanf(item," %d %d",&W,&H))<2) {
35562  if (!file) cimg::fclose(nfile);
35563  throw CImgIOException(_cimg_instance
35564  "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
35565  cimg_instance,
35566  filename?filename:"(FILE*)");
35567  }
35568  if (err==2) {
35569  while ((err=std::fscanf(nfile," %1023[^\n]",item))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
35570  if (std::sscanf(item,"%lf",&scale)!=1)
35571  cimg::warn(_cimg_instance
35572  "load_pfm(): SCALE field is undefined in file '%s'.",
35573  cimg_instance,
35574  filename?filename:"(FILE*)");
35575  }
35576  std::fgetc(nfile);
35577  const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
35578  if (is_color) {
35579  assign(W,H,1,3,0);
35580  CImg<floatT> buf(3*W);
35581  T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
35582  cimg_forY(*this,y) {
35583  cimg::fread(buf._data,3*W,nfile);
35584  if (is_inverted) cimg::invert_endianness(buf._data,3*W);
35585  const float *ptrs = buf._data;
35586  cimg_forX(*this,x) {
35587  *(ptr_r++) = (T)*(ptrs++);
35588  *(ptr_g++) = (T)*(ptrs++);
35589  *(ptr_b++) = (T)*(ptrs++);
35590  }
35591  }
35592  } else {
35593  assign(W,H,1,1,0);
35594  CImg<floatT> buf(W);
35595  T *ptrd = data(0,0,0,0);
35596  cimg_forY(*this,y) {
35597  cimg::fread(buf._data,W,nfile);
35598  if (is_inverted) cimg::invert_endianness(buf._data,W);
35599  const float *ptrs = buf._data;
35600  cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
35601  }
35602  }
35603  if (!file) cimg::fclose(nfile);
35604  return mirror('y'); // Most of the .pfm files are flipped along the y-axis.
35605  }
35606 
35608 
35613  CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
35614  return _load_rgb(0,filename,dimw,dimh);
35615  }
35616 
35618  static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
35619  return CImg<T>().load_rgb(filename,dimw,dimh);
35620  }
35621 
35623  CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
35624  return _load_rgb(file,0,dimw,dimh);
35625  }
35626 
35628  static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
35629  return CImg<T>().load_rgb(file,dimw,dimh);
35630  }
35631 
35632  CImg<T>& _load_rgb(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) {
35633  if (!file && !filename)
35634  throw CImgArgumentException(_cimg_instance
35635  "load_rgb(): Specified filename is (null).",
35636  cimg_instance);
35637 
35638  if (!dimw || !dimh) return assign();
35639  const long cimg_iobuffer = 12*1024*1024;
35640  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35641  CImg<ucharT> raw;
35642  assign(dimw,dimh,1,3);
35643  T
35644  *ptr_r = data(0,0,0,0),
35645  *ptr_g = data(0,0,0,1),
35646  *ptr_b = data(0,0,0,2);
35647  for (long to_read = (long)size(); to_read>0; ) {
35648  raw.assign(cimg::min(to_read,cimg_iobuffer));
35649  cimg::fread(raw._data,raw._width,nfile);
35650  to_read-=raw._width;
35651  const unsigned char *ptrs = raw._data;
35652  for (unsigned long off = raw._width/3UL; off; --off) {
35653  *(ptr_r++) = (T)*(ptrs++);
35654  *(ptr_g++) = (T)*(ptrs++);
35655  *(ptr_b++) = (T)*(ptrs++);
35656  }
35657  }
35658  if (!file) cimg::fclose(nfile);
35659  return *this;
35660  }
35661 
35663 
35668  CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
35669  return _load_rgba(0,filename,dimw,dimh);
35670  }
35671 
35673  static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
35674  return CImg<T>().load_rgba(filename,dimw,dimh);
35675  }
35676 
35678  CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
35679  return _load_rgba(file,0,dimw,dimh);
35680  }
35681 
35683  static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
35684  return CImg<T>().load_rgba(file,dimw,dimh);
35685  }
35686 
35687  CImg<T>& _load_rgba(std::FILE *const file, const char *const filename, const unsigned int dimw, const unsigned int dimh) {
35688  if (!file && !filename)
35689  throw CImgArgumentException(_cimg_instance
35690  "load_rgba(): Specified filename is (null).",
35691  cimg_instance);
35692 
35693  if (!dimw || !dimh) return assign();
35694  const long cimg_iobuffer = 12*1024*1024;
35695  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
35696  CImg<ucharT> raw;
35697  assign(dimw,dimh,1,4);
35698  T
35699  *ptr_r = data(0,0,0,0),
35700  *ptr_g = data(0,0,0,1),
35701  *ptr_b = data(0,0,0,2),
35702  *ptr_a = data(0,0,0,3);
35703  for (long to_read = (long)size(); to_read>0; ) {
35704  raw.assign(cimg::min(to_read,cimg_iobuffer));
35705  cimg::fread(raw._data,raw._width,nfile);
35706  to_read-=raw._width;
35707  const unsigned char *ptrs = raw._data;
35708  for (unsigned long off = raw._width/4UL; off; --off) {
35709  *(ptr_r++) = (T)*(ptrs++);
35710  *(ptr_g++) = (T)*(ptrs++);
35711  *(ptr_b++) = (T)*(ptrs++);
35712  *(ptr_a++) = (T)*(ptrs++);
35713  }
35714  }
35715  if (!file) cimg::fclose(nfile);
35716  return *this;
35717  }
35718 
35720 
35734  CImg<T>& load_tiff(const char *const filename,
35735  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35736  const unsigned int step_frame=1) {
35737  if (!filename)
35738  throw CImgArgumentException(_cimg_instance
35739  "load_tiff(): Specified filename is (null).",
35740  cimg_instance);
35741 
35742  const unsigned int
35743  nfirst_frame = first_frame<last_frame?first_frame:last_frame,
35744  nstep_frame = step_frame?step_frame:1;
35745  unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
35746 
35747 #ifndef cimg_use_tiff
35748  if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
35749  throw CImgArgumentException(_cimg_instance
35750  "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
35751  cimg_instance,
35752  filename);
35753  return load_other(filename);
35754 #else
35755  TIFF *tif = TIFFOpen(filename,"r");
35756  if (tif) {
35757  unsigned int nb_images = 0;
35758  do ++nb_images; while (TIFFReadDirectory(tif));
35759  if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
35760  cimg::warn(_cimg_instance
35761  "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
35762  cimg_instance,
35763  filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
35764 
35765  if (nfirst_frame>=nb_images) return assign();
35766  if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
35767  TIFFSetDirectory(tif,0);
35768  CImg<T> frame;
35769  for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
35770  frame._load_tiff(tif,l);
35771  if (l==nfirst_frame) assign(frame._width,frame._height,1+(nlast_frame-nfirst_frame)/nstep_frame,frame._spectrum);
35772  if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
35773  resize(cimg::max(frame._width,_width),cimg::max(frame._height,_height),-100,cimg::max(frame._spectrum,_spectrum),0);
35774  draw_image(0,0,(l-nfirst_frame)/nstep_frame,frame);
35775  }
35776  TIFFClose(tif);
35777  } else throw CImgIOException(_cimg_instance
35778  "load_tiff(): Failed to open file '%s'.",
35779  cimg_instance,
35780  filename);
35781  return *this;
35782 #endif
35783  }
35784 
35786  static CImg<T> get_load_tiff(const char *const filename,
35787  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
35788  const unsigned int step_frame=1) {
35789  return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame);
35790  }
35791 
35792  // (Original contribution by Jerome Boulanger).
35793 #ifdef cimg_use_tiff
35794  template<typename t>
35795  void _load_tiff_tiled_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
35796  t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
35797  if (buf) {
35798  for (unsigned int row = 0; row<ny; row+=th)
35799  for (unsigned int col = 0; col<nx; col+=tw) {
35800  if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
35801  _TIFFfree(buf); TIFFClose(tif);
35802  throw CImgIOException(_cimg_instance
35803  "load_tiff(): Invalid tile in file '%s'.",
35804  cimg_instance,
35805  TIFFFileName(tif));
35806  }
35807  const t *ptr = buf;
35808  for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
35809  for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
35810  for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
35811  (*this)(cc,rr,vv) = (T)(ptr[(rr-row)*th*samplesperpixel + (cc-col)*samplesperpixel + vv]);
35812  }
35813  _TIFFfree(buf);
35814  }
35815  }
35816 
35817  template<typename t>
35818  void _load_tiff_tiled_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny, const uint32 tw, const uint32 th) {
35819  t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
35820  if (buf) {
35821  for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
35822  for (unsigned int row = 0; row<ny; row+=th)
35823  for (unsigned int col = 0; col<nx; col+=tw) {
35824  if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
35825  _TIFFfree(buf); TIFFClose(tif);
35826  throw CImgIOException(_cimg_instance
35827  "load_tiff(): Invalid tile in file '%s'.",
35828  cimg_instance,
35829  TIFFFileName(tif));
35830  }
35831  const t *ptr = buf;
35832  for (unsigned int rr = row; rr<cimg::min((unsigned int)(row+th),(unsigned int)ny); ++rr)
35833  for (unsigned int cc = col; cc<cimg::min((unsigned int)(col+tw),(unsigned int)nx); ++cc)
35834  (*this)(cc,rr,vv) = (T)*(ptr++);
35835  }
35836  _TIFFfree(buf);
35837  }
35838  }
35839 
35840  template<typename t>
35841  void _load_tiff_contig(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
35842  t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
35843  if (buf) {
35844  uint32 row, rowsperstrip = (uint32)-1;
35845  TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
35846  for (row = 0; row<ny; row+= rowsperstrip) {
35847  uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
35848  tstrip_t strip = TIFFComputeStrip(tif, row, 0);
35849  if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
35850  _TIFFfree(buf); TIFFClose(tif);
35851  throw CImgIOException(_cimg_instance
35852  "load_tiff(): Invalid strip in file '%s'.",
35853  cimg_instance,
35854  TIFFFileName(tif));
35855  }
35856  const t *ptr = buf;
35857  for (unsigned int rr = 0; rr<nrow; ++rr)
35858  for (unsigned int cc = 0; cc<nx; ++cc)
35859  for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row+rr,vv) = (T)*(ptr++);
35860  }
35861  _TIFFfree(buf);
35862  }
35863  }
35864 
35865  template<typename t>
35866  void _load_tiff_separate(TIFF *const tif, const uint16 samplesperpixel, const uint32 nx, const uint32 ny) {
35867  t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
35868  if (buf) {
35869  uint32 row, rowsperstrip = (uint32)-1;
35870  TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
35871  for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
35872  for (row = 0; row<ny; row+= rowsperstrip) {
35873  uint32 nrow = (row+rowsperstrip>ny?ny-row:rowsperstrip);
35874  tstrip_t strip = TIFFComputeStrip(tif, row, vv);
35875  if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
35876  _TIFFfree(buf); TIFFClose(tif);
35877  throw CImgIOException(_cimg_instance
35878  "load_tiff(): Invalid strip in file '%s'.",
35879  cimg_instance,
35880  TIFFFileName(tif));
35881  }
35882  const t *ptr = buf;
35883  for (unsigned int rr = 0;rr<nrow; ++rr)
35884  for (unsigned int cc = 0; cc<nx; ++cc)
35885  (*this)(cc,row+rr,vv) = (T)*(ptr++);
35886  }
35887  _TIFFfree(buf);
35888  }
35889  }
35890 
35891  CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory) {
35892  if (!TIFFSetDirectory(tif,directory)) return assign();
35893  uint16 samplesperpixel, bitspersample, photo;
35894  uint16 sampleformat = SAMPLEFORMAT_UINT;
35895  uint32 nx,ny;
35896  const char *const filename = TIFFFileName(tif);
35897  TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
35898  TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
35899  TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
35900  TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
35901  TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
35902  TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
35903  int spectrum = samplesperpixel;
35904  if (photo == 3) spectrum = 3;
35905  assign(nx,ny,1,spectrum);
35906  if ((photo < 3) && ( bitspersample!=8 || !(samplesperpixel==3 || samplesperpixel==4))) {
35907  uint16 config;
35908  TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
35909  if (TIFFIsTiled(tif)) {
35910  uint32 tw, th;
35911  TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
35912  TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
35913  if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
35914  case 8 : {
35915  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
35916  else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
35917  } break;
35918  case 16 :
35919  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
35920  else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
35921  break;
35922  case 32 :
35923  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
35924  else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
35925  else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
35926  break;
35927  } else switch (bitspersample) {
35928  case 8 :
35929  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
35930  else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
35931  break;
35932  case 16 :
35933  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
35934  else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
35935  break;
35936  case 32 :
35937  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
35938  else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
35939  else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
35940  break;
35941  }
35942  } else {
35943  if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
35944  case 8 :
35945  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
35946  else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
35947  break;
35948  case 16 :
35949  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
35950  else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
35951  break;
35952  case 32 :
35953  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
35954  else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
35955  else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
35956  break;
35957  } else switch (bitspersample){
35958  case 8 :
35959  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
35960  else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
35961  break;
35962  case 16 :
35963  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
35964  else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
35965  break;
35966  case 32 :
35967  if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
35968  else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
35969  else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
35970  break;
35971  }
35972  }
35973  } else {
35974  uint32 *const raster = (uint32*)_TIFFmalloc(nx*ny*sizeof(uint32));
35975  if (!raster) {
35976  _TIFFfree(raster); TIFFClose(tif);
35977  throw CImgException(_cimg_instance
35978  "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
35979  cimg_instance,
35980  cimg::strbuffersize(nx*ny*sizeof(uint32)),filename);
35981  }
35982  TIFFReadRGBAImage(tif,nx,ny,raster,0);
35983  switch (spectrum) {
35984  case 1 : {
35985  cimg_forXY(*this,x,y) (*this)(x,y) = (T)(float)((raster[nx*(ny-1-y)+x] + 128)/257);
35986  } break;
35987  case 3 : {
35988  cimg_forXY(*this,x,y) {
35989  (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
35990  (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
35991  (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
35992  }
35993  } break;
35994  case 4 : {
35995  cimg_forXY(*this,x,y) {
35996  (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny-1-y)+x]);
35997  (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny-1-y)+x]);
35998  (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny-1-y)+x]);
35999  (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny-1-y)+x]);
36000  }
36001  } break;
36002  }
36003  _TIFFfree(raster);
36004  }
36005  return *this;
36006  }
36007 #endif
36008 
36010 
36013  // (Original code by Haz-Edine Assemlal).
36014  CImg<T>& load_minc2(const char *const filename) {
36015  if (!filename)
36016  throw CImgArgumentException(_cimg_instance
36017  "load_minc2(): Specified filename is (null).",
36018  cimg_instance);
36019 #ifndef cimg_use_minc2
36020  return load_other(filename);
36021 #else
36022  minc::minc_1_reader rdr;
36023  rdr.open(filename);
36024  assign(rdr.ndim(1)?rdr.ndim(1):1,
36025  rdr.ndim(2)?rdr.ndim(2):1,
36026  rdr.ndim(3)?rdr.ndim(3):1,
36027  rdr.ndim(4)?rdr.ndim(4):1);
36028  if(typeid(T)==typeid(unsigned char))
36029  rdr.setup_read_byte();
36030  else if(typeid(T)==typeid(int))
36031  rdr.setup_read_int();
36032  else if(typeid(T)==typeid(double))
36033  rdr.setup_read_double();
36034  else
36035  rdr.setup_read_float();
36036  minc::load_standard_volume(rdr, this->_data);
36037  return *this;
36038 #endif
36039  }
36040 
36042  static CImg<T> get_load_minc2(const char *const filename) {
36043  return CImg<T>().load_analyze(filename);
36044  }
36045 
36047 
36051  CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
36052  return _load_analyze(0,filename,voxel_size);
36053  }
36054 
36056  static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
36057  return CImg<T>().load_analyze(filename,voxel_size);
36058  }
36059 
36061  CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
36062  return _load_analyze(file,0,voxel_size);
36063  }
36064 
36066  static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
36067  return CImg<T>().load_analyze(file,voxel_size);
36068  }
36069 
36070  CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
36071  if (!file && !filename)
36072  throw CImgArgumentException(_cimg_instance
36073  "load_analyze(): Specified filename is (null).",
36074  cimg_instance);
36075 
36076  std::FILE *nfile_header = 0, *nfile = 0;
36077  if (!file) {
36078  char body[1024] = { 0 };
36079  const char *const ext = cimg::split_filename(filename,body);
36080  if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file.
36081  nfile_header = cimg::fopen(filename,"rb");
36082  std::sprintf(body + std::strlen(body),".img");
36083  nfile = cimg::fopen(body,"rb");
36084  } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file.
36085  nfile = cimg::fopen(filename,"rb");
36086  std::sprintf(body + std::strlen(body),".hdr");
36087  nfile_header = cimg::fopen(body,"rb");
36088  } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file.
36089  } else nfile_header = nfile = file; // File is a Niftii file.
36090  if (!nfile || !nfile_header)
36091  throw CImgIOException(_cimg_instance
36092  "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
36093  cimg_instance,
36094  filename?filename:"(FILE*)");
36095 
36096  // Read header.
36097  bool endian = false;
36098  unsigned int header_size;
36099  cimg::fread(&header_size,1,nfile_header);
36100  if (!header_size)
36101  throw CImgIOException(_cimg_instance
36102  "load_analyze(): Invalid zero-sized header in file '%s'.",
36103  cimg_instance,
36104  filename?filename:"(FILE*)");
36105 
36106  if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
36107  unsigned char *const header = new unsigned char[header_size];
36108  cimg::fread(header+4,header_size-4,nfile_header);
36109  if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
36110  if (endian) {
36111  cimg::invert_endianness((short*)(header+40),5);
36112  cimg::invert_endianness((short*)(header+70),1);
36113  cimg::invert_endianness((short*)(header+72),1);
36114  cimg::invert_endianness((float*)(header+76),4);
36115  cimg::invert_endianness((float*)(header+112),1);
36116  }
36117  unsigned short *dim = (unsigned short*)(header+40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
36118  if (!dim[0])
36119  cimg::warn(_cimg_instance
36120  "load_analyze(): File '%s' defines an image with zero dimensions.",
36121  cimg_instance,
36122  filename?filename:"(FILE*)");
36123 
36124  if (dim[0]>4)
36125  cimg::warn(_cimg_instance
36126  "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
36127  cimg_instance,
36128  filename?filename:"(FILE*)",dim[0]);
36129 
36130  if (dim[0]>=1) dimx = dim[1];
36131  if (dim[0]>=2) dimy = dim[2];
36132  if (dim[0]>=3) dimz = dim[3];
36133  if (dim[0]>=4) dimv = dim[4];
36134  float scalefactor = *(float*)(header+112); if (scalefactor==0) scalefactor=1;
36135  const unsigned short datatype = *(short*)(header+70);
36136  if (voxel_size) {
36137  const float *vsize = (float*)(header+76);
36138  voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
36139  }
36140  delete[] header;
36141 
36142  // Read pixel data.
36143  assign(dimx,dimy,dimz,dimv);
36144  switch (datatype) {
36145  case 2 : {
36146  unsigned char *const buffer = new unsigned char[dimx*dimy*dimz*dimv];
36147  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36148  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36149  delete[] buffer;
36150  } break;
36151  case 4 : {
36152  short *const buffer = new short[dimx*dimy*dimz*dimv];
36153  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36154  if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
36155  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36156  delete[] buffer;
36157  } break;
36158  case 8 : {
36159  int *const buffer = new int[dimx*dimy*dimz*dimv];
36160  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36161  if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
36162  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36163  delete[] buffer;
36164  } break;
36165  case 16 : {
36166  float *const buffer = new float[dimx*dimy*dimz*dimv];
36167  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36168  if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
36169  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36170  delete[] buffer;
36171  } break;
36172  case 64 : {
36173  double *const buffer = new double[dimx*dimy*dimz*dimv];
36174  cimg::fread(buffer,dimx*dimy*dimz*dimv,nfile);
36175  if (endian) cimg::invert_endianness(buffer,dimx*dimy*dimz*dimv);
36176  cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
36177  delete[] buffer;
36178  } break;
36179  default :
36180  if (!file) cimg::fclose(nfile);
36181  throw CImgIOException(_cimg_instance
36182  "load_analyze(): Unable to load datatype %d in file '%s'",
36183  cimg_instance,
36184  datatype,filename?filename:"(FILE*)");
36185  }
36186  if (!file) cimg::fclose(nfile);
36187  return *this;
36188  }
36189 
36191 
36196  CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
36197  CImgList<T> list;
36198  list.load_cimg(filename);
36199  if (list._width==1) return list[0].move_to(*this);
36200  return assign(list.get_append(axis,align));
36201  }
36202 
36204  static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
36205  return CImg<T>().load_cimg(filename,axis,align);
36206  }
36207 
36209  CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
36210  CImgList<T> list;
36211  list.load_cimg(file);
36212  if (list._width==1) return list[0].move_to(*this);
36213  return assign(list.get_append(axis,align));
36214  }
36215 
36217  static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
36218  return CImg<T>().load_cimg(file,axis,align);
36219  }
36220 
36222 
36237  CImg<T>& load_cimg(const char *const filename,
36238  const unsigned int n0, const unsigned int n1,
36239  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
36240  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
36241  const char axis='z', const float align=0) {
36242  CImgList<T> list;
36243  list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
36244  if (list._width==1) return list[0].move_to(*this);
36245  return assign(list.get_append(axis,align));
36246  }
36247 
36249  static CImg<T> get_load_cimg(const char *const filename,
36250  const unsigned int n0, const unsigned int n1,
36251  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
36252  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
36253  const char axis='z', const float align=0) {
36254  return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
36255  }
36256 
36258  CImg<T>& load_cimg(std::FILE *const file,
36259  const unsigned int n0, const unsigned int n1,
36260  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
36261  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
36262  const char axis='z', const float align=0) {
36263  CImgList<T> list;
36264  list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
36265  if (list._width==1) return list[0].move_to(*this);
36266  return assign(list.get_append(axis,align));
36267  }
36268 
36270  static CImg<T> get_load_cimg(std::FILE *const file,
36271  const unsigned int n0, const unsigned int n1,
36272  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
36273  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1,
36274  const char axis='z', const float align=0) {
36275  return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
36276  }
36277 
36279 
36283  CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
36284  return _load_inr(0,filename,voxel_size);
36285  }
36286 
36288  static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
36289  return CImg<T>().load_inr(filename,voxel_size);
36290  }
36291 
36293  CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
36294  return _load_inr(file,0,voxel_size);
36295  }
36296 
36298  static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
36299  return CImg<T>().load_inr(file,voxel_size);
36300  }
36301 
36302  static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
36303  char item[1024] = { 0 }, tmp1[64] = { 0 }, tmp2[64] = { 0 };
36304  out[0] = std::fscanf(file,"%63s",item);
36305  out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
36306  if(cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
36307  throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
36308  pixel_type());
36309 
36310  while (std::fscanf(file," %63[^\n]%*c",item)!=EOF && std::strncmp(item,"##}",3)) {
36311  std::sscanf(item," XDIM%*[^0-9]%d",out);
36312  std::sscanf(item," YDIM%*[^0-9]%d",out+1);
36313  std::sscanf(item," ZDIM%*[^0-9]%d",out+2);
36314  std::sscanf(item," VDIM%*[^0-9]%d",out+3);
36315  std::sscanf(item," PIXSIZE%*[^0-9]%d",out+6);
36316  if (voxel_size) {
36317  std::sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
36318  std::sscanf(item," VY%*[^0-9.+-]%f",voxel_size+1);
36319  std::sscanf(item," VZ%*[^0-9.+-]%f",voxel_size+2);
36320  }
36321  if (std::sscanf(item," CPU%*[ =]%s",tmp1)) out[7]=cimg::strncasecmp(tmp1,"sun",3)?0:1;
36322  switch (std::sscanf(item," TYPE%*[ =]%s %s",tmp1,tmp2)) {
36323  case 0 : break;
36324  case 2 : out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0; std::strncpy(tmp1,tmp2,sizeof(tmp1)-1);
36325  case 1 :
36326  if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0;
36327  if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
36328  if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
36329  if (out[4]>=0) break;
36330  default :
36331  throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
36332  pixel_type(),
36333  tmp2);
36334  }
36335  }
36336  if(out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
36337  throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
36338  pixel_type(),
36339  out[0],out[1],out[2],out[3]);
36340  if(out[4]<0 || out[5]<0)
36341  throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
36342  pixel_type());
36343  if(out[6]<0)
36344  throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
36345  pixel_type());
36346  if(out[7]<0)
36347  throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
36348  pixel_type());
36349  }
36350 
36351  CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
36352 #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
36353  if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
36354  Ts *xval, *const val = new Ts[fopt[0]*fopt[3]]; \
36355  cimg_forYZ(*this,y,z) { \
36356  cimg::fread(val,fopt[0]*fopt[3],nfile); \
36357  if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
36358  xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
36359  } \
36360  delete[] val; \
36361  loaded = true; \
36362  }
36363 
36364  if (!file && !filename)
36365  throw CImgArgumentException(_cimg_instance
36366  "load_inr(): Specified filename is (null).",
36367  cimg_instance);
36368 
36369  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
36370  int fopt[8], endian=cimg::endianness()?1:0;
36371  bool loaded = false;
36372  if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
36373  _load_inr_header(nfile,fopt,voxel_size);
36374  assign(fopt[0],fopt[1],fopt[2],fopt[3]);
36375  _cimg_load_inr_case(0,0,8,unsigned char);
36376  _cimg_load_inr_case(0,1,8,char);
36377  _cimg_load_inr_case(0,0,16,unsigned short);
36378  _cimg_load_inr_case(0,1,16,short);
36379  _cimg_load_inr_case(0,0,32,unsigned int);
36380  _cimg_load_inr_case(0,1,32,int);
36381  _cimg_load_inr_case(1,0,32,float);
36382  _cimg_load_inr_case(1,1,32,float);
36383  _cimg_load_inr_case(1,0,64,double);
36384  _cimg_load_inr_case(1,1,64,double);
36385  if (!loaded) {
36386  if (!file) cimg::fclose(nfile);
36387  throw CImgIOException(_cimg_instance
36388  "load_inr(): Unknown pixel type defined in file '%s'.",
36389  cimg_instance,
36390  filename?filename:"(FILE*)");
36391  }
36392  if (!file) cimg::fclose(nfile);
36393  return *this;
36394  }
36395 
36397 
36400  CImg<T>& load_exr(const char *const filename) {
36401  if (!filename)
36402  throw CImgArgumentException(_cimg_instance
36403  "load_exr(): Specified filename is (null).",
36404  cimg_instance);
36405 
36406 #ifndef cimg_use_openexr
36407  return load_other(filename);
36408 #else
36409  Imf::RgbaInputFile file(filename);
36410  Imath::Box2i dw = file.dataWindow();
36411  const int
36412  inwidth = dw.max.x - dw.min.x + 1,
36413  inheight = dw.max.y - dw.min.y + 1;
36414  Imf::Array2D<Imf::Rgba> pixels;
36415  pixels.resizeErase(inheight,inwidth);
36416  file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
36417  file.readPixels(dw.min.y, dw.max.y);
36418  assign(inwidth,inheight,1,4);
36419  T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
36420  cimg_forXY(*this,x,y) {
36421  *(ptr_r++) = (T)pixels[y][x].r;
36422  *(ptr_g++) = (T)pixels[y][x].g;
36423  *(ptr_b++) = (T)pixels[y][x].b;
36424  *(ptr_a++) = (T)pixels[y][x].a;
36425  }
36426  return *this;
36427 #endif
36428  }
36429 
36431  static CImg<T> get_load_exr(const char *const filename) {
36432  return CImg<T>().load_exr(filename);
36433  }
36434 
36436 
36439  CImg<T>& load_pandore(const char *const filename) {
36440  return _load_pandore(0,filename);
36441  }
36442 
36444  static CImg<T> get_load_pandore(const char *const filename) {
36445  return CImg<T>().load_pandore(filename);
36446  }
36447 
36449  CImg<T>& load_pandore(std::FILE *const file) {
36450  return _load_pandore(file,0);
36451  }
36452 
36454  static CImg<T> get_load_pandore(std::FILE *const file) {
36455  return CImg<T>().load_pandore(file);
36456  }
36457 
36458  CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
36459 #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
36460  cimg::fread(dims,nbdim,nfile); \
36461  if (endian) cimg::invert_endianness(dims,nbdim); \
36462  assign(nwidth,nheight,ndepth,ndim); \
36463  const unsigned int siz = size(); \
36464  stype *buffer = new stype[siz]; \
36465  cimg::fread(buffer,siz,nfile); \
36466  if (endian) cimg::invert_endianness(buffer,siz); \
36467  T *ptrd = _data; \
36468  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
36469  buffer-=siz; \
36470  delete[] buffer
36471 
36472 #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
36473  if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
36474  else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
36475  else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
36476  else throw CImgIOException(_cimg_instance \
36477  "load_pandore(): Unknown pixel datatype in file '%s'.", \
36478  cimg_instance, \
36479  filename?filename:"(FILE*)"); }
36480 
36481  if (!file && !filename)
36482  throw CImgArgumentException(_cimg_instance
36483  "load_pandore(): Specified filename is (null).",
36484  cimg_instance);
36485 
36486  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
36487  char header[32] = { 0 };
36488  cimg::fread(header,12,nfile);
36489  if (cimg::strncasecmp("PANDORE",header,7)) {
36490  if (!file) cimg::fclose(nfile);
36491  throw CImgIOException(_cimg_instance
36492  "load_pandore(): PANDORE header not found in file '%s'.",
36493  cimg_instance,
36494  filename?filename:"(FILE*)");
36495  }
36496  unsigned int imageid, dims[8] = { 0 };
36497  cimg::fread(&imageid,1,nfile);
36498  const bool endian = (imageid>255);
36499  if (endian) cimg::invert_endianness(imageid);
36500  cimg::fread(header,20,nfile);
36501 
36502  switch (imageid) {
36503  case 2: _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
36504  case 3: _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
36505  case 4: _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
36506  case 5: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
36507  case 6: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
36508  case 7: _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
36509  case 8: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
36510  case 9: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
36511  case 10: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
36512  case 11 : { // Region 1d
36513  cimg::fread(dims,3,nfile);
36514  if (endian) cimg::invert_endianness(dims,3);
36515  assign(dims[1],1,1,1);
36516  const unsigned siz = size();
36517  if (dims[2]<256) {
36518  unsigned char *buffer = new unsigned char[siz];
36519  cimg::fread(buffer,siz,nfile);
36520  T *ptrd = _data;
36521  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36522  buffer-=siz;
36523  delete[] buffer;
36524  } else {
36525  if (dims[2]<65536) {
36526  unsigned short *buffer = new unsigned short[siz];
36527  cimg::fread(buffer,siz,nfile);
36528  if (endian) cimg::invert_endianness(buffer,siz);
36529  T *ptrd = _data;
36530  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36531  buffer-=siz;
36532  delete[] buffer;
36533  } else {
36534  unsigned int *buffer = new unsigned int[siz];
36535  cimg::fread(buffer,siz,nfile);
36536  if (endian) cimg::invert_endianness(buffer,siz);
36537  T *ptrd = _data;
36538  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36539  buffer-=siz;
36540  delete[] buffer;
36541  }
36542  }
36543  }
36544  break;
36545  case 12 : { // Region 2d
36546  cimg::fread(dims,4,nfile);
36547  if (endian) cimg::invert_endianness(dims,4);
36548  assign(dims[2],dims[1],1,1);
36549  const unsigned int siz = size();
36550  if (dims[3]<256) {
36551  unsigned char *buffer = new unsigned char[siz];
36552  cimg::fread(buffer,siz,nfile);
36553  T *ptrd = _data;
36554  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36555  buffer-=siz;
36556  delete[] buffer;
36557  } else {
36558  if (dims[3]<65536) {
36559  unsigned short *buffer = new unsigned short[siz];
36560  cimg::fread(buffer,siz,nfile);
36561  if (endian) cimg::invert_endianness(buffer,siz);
36562  T *ptrd = _data;
36563  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36564  buffer-=siz;
36565  delete[] buffer;
36566  } else {
36567  unsigned long *buffer = new unsigned long[siz];
36568  cimg::fread(buffer,siz,nfile);
36569  if (endian) cimg::invert_endianness(buffer,siz);
36570  T *ptrd = _data;
36571  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36572  buffer-=siz;
36573  delete[] buffer;
36574  }
36575  }
36576  }
36577  break;
36578  case 13 : { // Region 3d
36579  cimg::fread(dims,5,nfile);
36580  if (endian) cimg::invert_endianness(dims,5);
36581  assign(dims[3],dims[2],dims[1],1);
36582  const unsigned int siz = size();
36583  if (dims[4]<256) {
36584  unsigned char *buffer = new unsigned char[siz];
36585  cimg::fread(buffer,siz,nfile);
36586  T *ptrd = _data;
36587  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36588  buffer-=siz;
36589  delete[] buffer;
36590  } else {
36591  if (dims[4]<65536) {
36592  unsigned short *buffer = new unsigned short[siz];
36593  cimg::fread(buffer,siz,nfile);
36594  if (endian) cimg::invert_endianness(buffer,siz);
36595  T *ptrd = _data;
36596  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36597  buffer-=siz;
36598  delete[] buffer;
36599  } else {
36600  unsigned int *buffer = new unsigned int[siz];
36601  cimg::fread(buffer,siz,nfile);
36602  if (endian) cimg::invert_endianness(buffer,siz);
36603  T *ptrd = _data;
36604  cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
36605  buffer-=siz;
36606  delete[] buffer;
36607  }
36608  }
36609  }
36610  break;
36611  case 16: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
36612  case 17: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
36613  case 18: _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
36614  case 19: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
36615  case 20: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
36616  case 21: _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
36617  case 22: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
36618  case 23: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4);
36619  case 24: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
36620  case 25: _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
36621  case 26: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
36622  case 27: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
36623  case 28: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
36624  case 29: _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
36625  case 30: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1); break;
36626  case 31: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
36627  case 32: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4); break;
36628  case 33: _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
36629  case 34 : { // Points 1d
36630  int ptbuf[4] = { 0 };
36631  cimg::fread(ptbuf,1,nfile);
36632  if (endian) cimg::invert_endianness(ptbuf,1);
36633  assign(1); (*this)(0) = (T)ptbuf[0];
36634  } break;
36635  case 35 : { // Points 2d
36636  int ptbuf[4] = { 0 };
36637  cimg::fread(ptbuf,2,nfile);
36638  if (endian) cimg::invert_endianness(ptbuf,2);
36639  assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
36640  } break;
36641  case 36 : { // Points 3d
36642  int ptbuf[4] = { 0 };
36643  cimg::fread(ptbuf,3,nfile);
36644  if (endian) cimg::invert_endianness(ptbuf,3);
36645  assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
36646  } break;
36647  default :
36648  if (!file) cimg::fclose(nfile);
36649  throw CImgIOException(_cimg_instance
36650  "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
36651  cimg_instance,
36652  imageid,filename?filename:"(FILE*)");
36653  }
36654  if (!file) cimg::fclose(nfile);
36655  return *this;
36656  }
36657 
36659 
36664  CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
36665  CImgList<T> list;
36666  list.load_parrec(filename);
36667  if (list._width==1) return list[0].move_to(*this);
36668  return assign(list.get_append(axis,align));
36669  }
36670 
36672  static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
36673  return CImg<T>().load_parrec(filename,axis,align);
36674  }
36675 
36677 
36686  CImg<T>& load_raw(const char *const filename,
36687  const unsigned int size_x=0, const unsigned int size_y=1,
36688  const unsigned int size_z=1, const unsigned int size_c=1,
36689  const bool is_multiplexed=false, const bool invert_endianness=false) {
36690  return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness);
36691  }
36692 
36694  static CImg<T> get_load_raw(const char *const filename,
36695  const unsigned int size_x=0, const unsigned int size_y=1,
36696  const unsigned int size_z=1, const unsigned int size_c=1,
36697  const bool is_multiplexed=false, const bool invert_endianness=false) {
36698  return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness);
36699  }
36700 
36702  CImg<T>& load_raw(std::FILE *const file,
36703  const unsigned int size_x=0, const unsigned int size_y=1,
36704  const unsigned int size_z=1, const unsigned int size_c=1,
36705  const bool is_multiplexed=false, const bool invert_endianness=false) {
36706  return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness);
36707  }
36708 
36710  static CImg<T> get_load_raw(std::FILE *const file,
36711  const unsigned int size_x=0, const unsigned int size_y=1,
36712  const unsigned int size_z=1, const unsigned int size_c=1,
36713  const bool is_multiplexed=false, const bool invert_endianness=false) {
36714  return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness);
36715  }
36716 
36717  CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
36718  const unsigned int size_x, const unsigned int size_y,
36719  const unsigned int size_z, const unsigned int size_c,
36720  const bool is_multiplexed, const bool invert_endianness) {
36721  if (!file && !filename)
36722  throw CImgArgumentException(_cimg_instance
36723  "load_raw(): Specified filename is (null).",
36724  cimg_instance);
36725  unsigned int siz = size_x*size_y*size_z*size_c, _size_x = size_x, _size_y = size_y, _size_z = size_z, _size_c = size_c;
36726  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
36727  if (!siz) { // Retrieve file size.
36728  const long fpos = std::ftell(nfile);
36729  if (fpos<0) throw CImgArgumentException(_cimg_instance
36730  "load_raw(): Cannot determine size of input file '%s'.",
36731  cimg_instance,filename?filename:"(FILE*)");
36732  std::fseek(nfile,0,SEEK_END);
36733  siz = _size_y = (unsigned int)std::ftell(nfile)/sizeof(T);
36734  _size_x = _size_z = _size_c = 1;
36735  std::fseek(nfile,fpos,SEEK_SET);
36736  }
36737  assign(_size_x,_size_y,_size_z,_size_c,0);
36738  if (!is_multiplexed || size_c==1) {
36739  cimg::fread(_data,siz,nfile);
36740  if (invert_endianness) cimg::invert_endianness(_data,siz);
36741  } else {
36742  CImg<T> buf(1,1,1,_size_c);
36743  cimg_forXYZ(*this,x,y,z) {
36744  cimg::fread(buf._data,_size_c,nfile);
36745  if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
36746  set_vector_at(buf,x,y,z);
36747  }
36748  }
36749  if (!file) cimg::fclose(nfile);
36750  return *this;
36751  }
36752 
36754 
36764  CImg<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36765  const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
36766  const char axis='z', const float align=0) {
36767  return get_load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume,axis,align).move_to(*this);
36768  }
36769 
36771  static CImg<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36772  const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false,
36773  const char axis='z', const float align=0) {
36774  return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format,resume).get_append(axis,align);
36775  }
36776 
36778 
36788  CImg<T>& load_yuv(const char *const filename,
36789  const unsigned int size_x, const unsigned int size_y=1,
36790  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36791  const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
36792  return get_load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
36793  }
36794 
36796  static CImg<T> get_load_yuv(const char *const filename,
36797  const unsigned int size_x, const unsigned int size_y=1,
36798  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36799  const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
36800  return CImgList<T>().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
36801  }
36802 
36804  CImg<T>& load_yuv(std::FILE *const file,
36805  const unsigned int size_x, const unsigned int size_y=1,
36806  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36807  const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
36808  return get_load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
36809  }
36810 
36812  static CImg<T> get_load_yuv(std::FILE *const file,
36813  const unsigned int size_x, const unsigned int size_y=1,
36814  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
36815  const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
36816  return CImgList<T>().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
36817  }
36818 
36820 
36825  template<typename tf, typename tc>
36826  CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
36827  return _load_off(primitives,colors,0,filename);
36828  }
36829 
36831  template<typename tf, typename tc>
36832  static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
36833  return CImg<T>().load_off(primitives,colors,filename);
36834  }
36835 
36837  template<typename tf, typename tc>
36838  CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
36839  return _load_off(primitives,colors,file,0);
36840  }
36841 
36843  template<typename tf, typename tc>
36844  static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
36845  return CImg<T>().load_off(primitives,colors,file);
36846  }
36847 
36848  template<typename tf, typename tc>
36849  CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
36850  std::FILE *const file, const char *const filename) {
36851  if (!file && !filename)
36852  throw CImgArgumentException(_cimg_instance
36853  "load_off(): Specified filename is (null).",
36854  cimg_instance);
36855 
36856  std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
36857  unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
36858  char line[256] = { 0 };
36859  int err;
36860 
36861  // Skip comments, and read magic string OFF
36862  do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
36863  if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
36864  if (!file) cimg::fclose(nfile);
36865  throw CImgIOException(_cimg_instance
36866  "load_off(): OFF header not found in file '%s'.",
36867  cimg_instance,
36868  filename?filename:"(FILE*)");
36869  }
36870  do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
36871  if ((err = std::sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
36872  if (!file) cimg::fclose(nfile);
36873  throw CImgIOException(_cimg_instance
36874  "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
36875  cimg_instance,
36876  filename?filename:"(FILE*)");
36877  }
36878 
36879  // Read points data
36880  assign(nb_points,3);
36881  float X = 0, Y = 0, Z = 0;
36882  cimg_forX(*this,l) {
36883  do { err = std::fscanf(nfile,"%255[^\n] ",line); } while (!err || (err==1 && *line=='#'));
36884  if ((err = std::sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
36885  if (!file) cimg::fclose(nfile);
36886  throw CImgIOException(_cimg_instance
36887  "load_off(): Failed to read vertex %u/%u in file '%s'.",
36888  cimg_instance,
36889  l+1,nb_points,filename?filename:"(FILE*)");
36890  }
36891  (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
36892  }
36893 
36894  // Read primitive data
36895  primitives.assign();
36896  colors.assign();
36897  bool stopflag = false;
36898  while (!stopflag) {
36899  float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
36900  unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
36901  *line = 0;
36902  if ((err = std::fscanf(nfile,"%u",&prim))!=1) stopflag=true;
36903  else {
36904  ++nb_read;
36905  switch (prim) {
36906  case 1 : {
36907  if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line))<2) {
36908  cimg::warn(_cimg_instance
36909  "load_off(): Failed to read primitive %u/%u from file '%s'.",
36910  cimg_instance,
36911  nb_read,nb_primitives,filename?filename:"(FILE*)");
36912 
36913  err = std::fscanf(nfile,"%*[^\n] ");
36914  } else {
36915  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36916  CImg<tf>::vector(i0).move_to(primitives);
36917  CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
36918  }
36919  } break;
36920  case 2 : {
36921  if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line))<2) {
36922  cimg::warn(_cimg_instance
36923  "load_off(): Failed to read primitive %u/%u from file '%s'.",
36924  cimg_instance,
36925  nb_read,nb_primitives,filename?filename:"(FILE*)");
36926 
36927  err = std::fscanf(nfile,"%*[^\n] ");
36928  } else {
36929  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36930  CImg<tf>::vector(i0,i1).move_to(primitives);
36931  CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
36932  }
36933  } break;
36934  case 3 : {
36935  if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line))<3) {
36936  cimg::warn(_cimg_instance
36937  "load_off(): Failed to read primitive %u/%u from file '%s'.",
36938  cimg_instance,
36939  nb_read,nb_primitives,filename?filename:"(FILE*)");
36940 
36941  err = std::fscanf(nfile,"%*[^\n] ");
36942  } else {
36943  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36944  CImg<tf>::vector(i0,i2,i1).move_to(primitives);
36945  CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
36946  }
36947  } break;
36948  case 4 : {
36949  if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line))<4) {
36950  cimg::warn(_cimg_instance
36951  "load_off(): Failed to read primitive %u/%u from file '%s'.",
36952  cimg_instance,
36953  nb_read,nb_primitives,filename?filename:"(FILE*)");
36954 
36955  err = std::fscanf(nfile,"%*[^\n] ");
36956  } else {
36957  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36958  CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
36959  CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
36960  }
36961  } break;
36962  case 5 : {
36963  if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line))<5) {
36964  cimg::warn(_cimg_instance
36965  "load_off(): Failed to read primitive %u/%u from file '%s'.",
36966  cimg_instance,
36967  nb_read,nb_primitives,filename?filename:"(FILE*)");
36968 
36969  err = std::fscanf(nfile,"%*[^\n] ");
36970  } else {
36971  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36972  CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
36973  CImg<tf>::vector(i0,i4,i3).move_to(primitives);
36974  colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
36975  ++nb_primitives;
36976  }
36977  } break;
36978  case 6 : {
36979  if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line))<6) {
36980  cimg::warn(_cimg_instance
36981  "load_off(): Failed to read primitive %u/%u from file '%s'.",
36982  cimg_instance,
36983  nb_read,nb_primitives,filename?filename:"(FILE*)");
36984 
36985  err = std::fscanf(nfile,"%*[^\n] ");
36986  } else {
36987  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
36988  CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
36989  CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
36990  colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
36991  ++nb_primitives;
36992  }
36993  } break;
36994  case 7 : {
36995  if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line))<7) {
36996  cimg::warn(_cimg_instance
36997  "load_off(): Failed to read primitive %u/%u from file '%s'.",
36998  cimg_instance,
36999  nb_read,nb_primitives,filename?filename:"(FILE*)");
37000 
37001  err = std::fscanf(nfile,"%*[^\n] ");
37002  } else {
37003  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
37004  CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
37005  CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
37006  CImg<tf>::vector(i3,i2,i1).move_to(primitives);
37007  colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
37008  ++(++nb_primitives);
37009  }
37010  } break;
37011  case 8 : {
37012  if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line))<7) {
37013  cimg::warn(_cimg_instance
37014  "load_off(): Failed to read primitive %u/%u from file '%s'.",
37015  cimg_instance,
37016  nb_read,nb_primitives,filename?filename:"(FILE*)");
37017 
37018  err = std::fscanf(nfile,"%*[^\n] ");
37019  } else {
37020  err = std::sscanf(line,"%f%f%f",&c0,&c1,&c2);
37021  CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
37022  CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
37023  CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
37024  colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
37025  ++(++nb_primitives);
37026  }
37027  } break;
37028  default :
37029  cimg::warn(_cimg_instance
37030  "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
37031  cimg_instance,
37032  nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
37033 
37034  err = std::fscanf(nfile,"%*[^\n] ");
37035  }
37036  }
37037  }
37038  if (!file) cimg::fclose(nfile);
37039  if (primitives._width!=nb_primitives)
37040  cimg::warn(_cimg_instance
37041  "load_off(): Only %u/%u primitives read from file '%s'.",
37042  cimg_instance,
37043  primitives._width,nb_primitives,filename?filename:"(FILE*)");
37044  return *this;
37045  }
37046 
37048 
37053  CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
37054  return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
37055  }
37056 
37058  static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
37059  return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
37060  }
37061 
37063 
37066  CImg<T>& load_graphicsmagick_external(const char *const filename) {
37067  if (!filename)
37068  throw CImgArgumentException(_cimg_instance
37069  "load_graphicsmagick_external(): Specified filename is (null).",
37070  cimg_instance);
37071  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
37072  char command[1024] = { 0 }, filetmp[512] = { 0 };
37073  std::FILE *file = 0;
37074 #if cimg_OS==1
37075  cimg_snprintf(command,sizeof(command),"%s convert \"%s\" pnm:-",cimg::graphicsmagick_path(),filename);
37076  file = popen(command,"r");
37077  if (file) {
37078  try { load_pnm(file); } catch (...) {
37079  pclose(file);
37080  throw CImgIOException(_cimg_instance
37081  "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
37082  cimg_instance,
37083  filename);
37084  }
37085  pclose(file);
37086  return *this;
37087  }
37088 #endif
37089  do {
37090  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37091  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37092  } while (file);
37093  cimg_snprintf(command,sizeof(command),"%s convert \"%s\" \"%s\"",cimg::graphicsmagick_path(),filename,filetmp);
37095  if (!(file = std::fopen(filetmp,"rb"))) {
37096  cimg::fclose(cimg::fopen(filename,"r"));
37097  throw CImgIOException(_cimg_instance
37098  "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
37099  cimg_instance,
37100  filename);
37101 
37102  } else cimg::fclose(file);
37103  load_pnm(filetmp);
37104  std::remove(filetmp);
37105  return *this;
37106  }
37107 
37109  static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
37110  return CImg<T>().load_graphicsmagick_external(filename);
37111  }
37112 
37114 
37117  CImg<T>& load_gzip_external(const char *const filename) {
37118  if (!filename)
37119  throw CImgIOException(_cimg_instance
37120  "load_gzip_external(): Specified filename is (null).",
37121  cimg_instance);
37122  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
37123  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
37124  const char
37125  *const ext = cimg::split_filename(filename,body),
37126  *const ext2 = cimg::split_filename(body,0);
37127 
37128  std::FILE *file = 0;
37129  do {
37130  if (!cimg::strcasecmp(ext,"gz")) {
37131  if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
37132  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37133  } else {
37134  if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
37135  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37136  }
37137  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37138  } while (file);
37139 
37140  cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp);
37141  cimg::system(command);
37142  if (!(file = std::fopen(filetmp,"rb"))) {
37143  cimg::fclose(cimg::fopen(filename,"r"));
37144  throw CImgIOException(_cimg_instance
37145  "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
37146  cimg_instance,
37147  filename);
37148 
37149  } else cimg::fclose(file);
37150  load(filetmp);
37151  std::remove(filetmp);
37152  return *this;
37153  }
37154 
37156  static CImg<T> get_load_gzip_external(const char *const filename) {
37157  return CImg<T>().load_gzip_external(filename);
37158  }
37159 
37161 
37164  CImg<T>& load_imagemagick_external(const char *const filename) {
37165  if (!filename)
37166  throw CImgArgumentException(_cimg_instance
37167  "load_imagemagick_external(): Specified filename is (null).",
37168  cimg_instance);
37169  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
37170  char command[1024] = { 0 }, filetmp[512] = { 0 };
37171  std::FILE *file = 0;
37172 #if cimg_OS==1
37173  cimg_snprintf(command,sizeof(command),"%s \"%s\" pnm:-",cimg::imagemagick_path(),filename);
37174  file = popen(command,"r");
37175  if (file) {
37176  try { load_pnm(file); } catch (...) {
37177  pclose(file);
37178  throw CImgIOException(_cimg_instance
37179  "load_imagemagick_external(): Failed to load file '%s' with external command 'convert'.",
37180  cimg_instance,
37181  filename);
37182  }
37183  pclose(file);
37184  return *this;
37185  }
37186 #endif
37187  do {
37188  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.pnm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37189  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37190  } while (file);
37191  cimg_snprintf(command,sizeof(command),"%s \"%s\" \"%s\"",cimg::imagemagick_path(),filename,filetmp);
37193  if (!(file = std::fopen(filetmp,"rb"))) {
37194  cimg::fclose(cimg::fopen(filename,"r"));
37195  throw CImgIOException(_cimg_instance
37196  "load_imagemagick_external(): Failed to load file '%s' with external command 'convert'.",
37197  cimg_instance,
37198  filename);
37199 
37200  } else cimg::fclose(file);
37201  load_pnm(filetmp);
37202  std::remove(filetmp);
37203  return *this;
37204  }
37205 
37207  static CImg<T> get_load_imagemagick_external(const char *const filename) {
37208  return CImg<T>().load_imagemagick_external(filename);
37209  }
37210 
37212 
37215  CImg<T>& load_medcon_external(const char *const filename) {
37216  if (!filename)
37217  throw CImgArgumentException(_cimg_instance
37218  "load_medcon_external(): Specified filename is (null).",
37219  cimg_instance);
37220  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
37221  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
37222  cimg::fclose(cimg::fopen(filename,"r"));
37223  std::FILE *file = 0;
37224  do {
37225  cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand());
37226  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37227  } while (file);
37228  cimg_snprintf(command,sizeof(command),"%s -w -c anlz -o %s -f %s",cimg::medcon_path(),filetmp,filename);
37229  cimg::system(command);
37230  cimg::split_filename(filetmp,body);
37231 
37232  cimg_snprintf(command,sizeof(command),"%s.hdr",body);
37233  file = std::fopen(command,"rb");
37234  if (!file) {
37235  cimg_snprintf(command,sizeof(command),"m000-%s.hdr",body);
37236  file = std::fopen(command,"rb");
37237  if (!file) {
37238  throw CImgIOException(_cimg_instance
37239  "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
37240  cimg_instance,
37241  filename);
37242  }
37243  }
37244  cimg::fclose(file);
37245  load_analyze(command);
37246  std::remove(command);
37247  cimg::split_filename(command,body);
37248  cimg_snprintf(command,sizeof(command),"%s.img",body);
37249  std::remove(command);
37250  return *this;
37251  }
37252 
37254  static CImg<T> get_load_medcon_external(const char *const filename) {
37255  return CImg<T>().load_medcon_external(filename);
37256  }
37257 
37259 
37262  CImg<T>& load_dcraw_external(const char *const filename) {
37263  if (!filename)
37264  throw CImgArgumentException(_cimg_instance
37265  "load_dcraw_external(): Specified filename is (null).",
37266  cimg_instance);
37267  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
37268  char command[1024] = { 0 }, filetmp[512] = { 0 };
37269  std::FILE *file = 0;
37270 #if cimg_OS==1
37271  cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\"",cimg::dcraw_path(),filename);
37272  file = popen(command,"r");
37273  if (file) {
37274  try { load_pnm(file); } catch (...) {
37275  pclose(file);
37276  throw CImgIOException(_cimg_instance
37277  "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
37278  cimg_instance,
37279  filename);
37280  }
37281  pclose(file);
37282  return *this;
37283  }
37284 #endif
37285  do {
37286  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.ppm",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
37287  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
37288  } while (file);
37289  cimg_snprintf(command,sizeof(command),"%s -w -4 -c \"%s\" > %s",cimg::dcraw_path(),filename,filetmp);
37290  cimg::system(command,cimg::dcraw_path());
37291  if (!(file = std::fopen(filetmp,"rb"))) {
37292  cimg::fclose(cimg::fopen(filename,"r"));
37293  throw CImgIOException(_cimg_instance
37294  "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
37295  cimg_instance,
37296  filename);
37297 
37298  } else cimg::fclose(file);
37299  load_pnm(filetmp);
37300  std::remove(filetmp);
37301  return *this;
37302  }
37303 
37305  static CImg<T> get_load_dcraw_external(const char *const filename) {
37306  return CImg<T>().load_dcraw_external(filename);
37307  }
37308 
37310 
37315  CImg<T>& load_camera(const int camera_index=-1, const unsigned int skip_frames=0, const bool release_camera=false) {
37316 #ifdef cimg_use_opencv
37317  const int ind = camera_index + 1;
37318  if (ind<0 || ind>255)
37319  throw CImgArgumentException(_cimg_instance
37320  "load_camera(): Invalid request for camera #%d.",
37321  cimg_instance,
37322  camera_index);
37323  static CvCapture *capture[256] = { 0 };
37324  if (release_camera) {
37325  if (capture[ind]) cvReleaseCapture(&(capture[ind]));
37326  capture[ind] = 0;
37327  return *this;
37328  }
37329  if (!capture[ind]) {
37330  capture[ind] = cvCreateCameraCapture(camera_index);
37331  if (!capture[ind]) {
37332  if (camera_index>=0)
37333  throw CImgIOException(_cimg_instance
37334  "load_camera(): Failed to initialize camera #%d.",
37335  cimg_instance,
37336  camera_index);
37337  else
37338  throw CImgIOException(_cimg_instance
37339  "load_camera(): Failed to initialize default camera.",
37340  cimg_instance);
37341  }
37342  }
37343  const IplImage *img = 0;
37344  for (unsigned int i = 0; i<skip_frames; ++i) img = cvQueryFrame(capture[ind]);
37345  img = cvQueryFrame(capture[ind]);
37346  if (img) {
37347  const int step = (int)(img->widthStep - 3*img->width);
37348  assign(img->width,img->height,1,3);
37349  const unsigned char* ptrs = (unsigned char*)img->imageData;
37350  T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
37351  if (step>0) cimg_forY(*this,y) {
37352  cimg_forX(*this,x) { *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++); }
37353  ptrs+=step;
37354  } else for (unsigned long siz = (unsigned long)img->width*img->height; siz; --siz) {
37355  *(ptr_b++) = (T)*(ptrs++); *(ptr_g++) = (T)*(ptrs++); *(ptr_r++) = (T)*(ptrs++);
37356  }
37357  }
37358 #else
37359  cimg::unused(camera_index,skip_frames,release_camera);
37360  throw CImgIOException(_cimg_instance
37361  "load_camera(): This function requires the OpenCV library to run "
37362  "(macro 'cimg_use_opencv' must be defined).",
37363  cimg_instance);
37364 #endif
37365  return *this;
37366  }
37367 
37369  static CImg<T> get_load_camera(const int camera_index=-1, const unsigned int skip_frames=0, const bool release_camera=false) {
37370  return CImg<T>().load_camera(camera_index,skip_frames,release_camera);
37371  }
37372 
37374 
37377  CImg<T>& load_other(const char *const filename) {
37378  if (!filename)
37379  throw CImgArgumentException(_cimg_instance
37380  "load_other(): Specified filename is (null).",
37381  cimg_instance);
37382 
37383  const unsigned int omode = cimg::exception_mode();
37384  cimg::exception_mode() = 0;
37385  try { load_magick(filename); }
37386  catch (CImgException&) {
37387  try { load_imagemagick_external(filename); }
37388  catch (CImgException&) {
37389  try { load_graphicsmagick_external(filename); }
37390  catch (CImgException&) {
37391  try { load_cimg(filename); }
37392  catch (CImgException&) {
37393  try {
37394  std::fclose(cimg::fopen(filename,"rb"));
37395  cimg::exception_mode() = omode;
37396  throw CImgIOException(_cimg_instance
37397  "load_other(): Failed to recognize format of file '%s'.",
37398  cimg_instance,
37399  filename);
37400  } catch (CImgException&) {
37401  cimg::exception_mode() = omode;
37402  throw CImgIOException(_cimg_instance
37403  "load_other(): Failed to open file '%s'.",
37404  cimg_instance,
37405  filename);
37406  }
37407  }
37408  }
37409  }
37410  }
37411  cimg::exception_mode() = omode;
37412  return *this;
37413  }
37414 
37416  static CImg<T> get_load_other(const char *const filename) {
37417  return CImg<T>().load_other(filename);
37418  }
37419 
37421  //---------------------------
37422  //
37424 
37425  //---------------------------
37426 
37428 
37432  const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
37433  int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
37434  static CImg<doubleT> st;
37435  if (!is_empty() && display_stats) {
37436  st = get_stats();
37437  xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
37438  xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
37439  }
37440  const unsigned long siz = size(), msiz = siz*sizeof(T), siz1 = siz-1, mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2), width1 = _width-1;
37441 
37442  char _title[64] = { 0 };
37443  if (!title) cimg_snprintf(_title,sizeof(_title),"CImg<%s>",pixel_type());
37444 
37445  std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
37446  cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal,
37447  cimg::t_bold,cimg::t_normal,(void*)this,
37448  cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
37449  mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
37450  mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
37451  cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
37452  if (_data) std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end()-1),_is_shared?"shared":"non-shared");
37453  else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
37454 
37455  if (!is_empty()) cimg_foroff(*this,off) {
37456  std::fprintf(cimg::output(),cimg::type<T>::format(),cimg::type<T>::format(_data[off]));
37457  if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
37458  if (off==7 && siz>16) { off = siz1-8; if (off!=7) std::fprintf(cimg::output(),"... "); }
37459  }
37460  if (!is_empty() && display_stats)
37461  std::fprintf(cimg::output()," ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), %scoords_max%s = (%u,%u,%u,%u).\n",
37462  cimg::t_bold,cimg::t_normal,st[0],
37463  cimg::t_bold,cimg::t_normal,st[1],
37464  cimg::t_bold,cimg::t_normal,st[2],
37465  cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
37466  cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
37467  cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
37468  else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
37469  std::fflush(cimg::output());
37470  return *this;
37471  }
37472 
37474 
37477  const CImg<T>& display(CImgDisplay& disp) const {
37478  disp.display(*this);
37479  return *this;
37480  }
37481 
37483 
37487  const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0) const {
37488  return _display(disp,0,display_info,XYZ,false);
37489  }
37490 
37492 
37496  const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0) const {
37497  CImgDisplay disp;
37498  return _display(disp,title,display_info,XYZ,false);
37499  }
37500 
37501  const CImg<T>& _display(CImgDisplay &disp, const char *const title,
37502  const bool display_info, unsigned int *const XYZ,
37503  const bool exit_on_simpleclick) const {
37504  if (is_empty())
37505  throw CImgInstanceException(_cimg_instance
37506  "display(): Empty instance.",
37507  cimg_instance);
37508 
37509  unsigned int oldw = 0, oldh = 0, _XYZ[3], key = 0;
37510  int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1;
37511 
37512  if (!disp) {
37513  disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
37514  if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
37515  } else if (title) disp.set_title("%s",title);
37516  disp.show().flush();
37517 
37518  const CImg<char> dtitle = CImg<char>::string(disp.title());
37519  if (display_info) print(dtitle);
37520 
37521  CImg<T> zoom;
37522  for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
37523  if (reset_view) {
37524  if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
37525  else { _XYZ[0] = (x0 + x1)/2; _XYZ[1] = (y0 + y1)/2; _XYZ[2] = (z0 + z1)/2; }
37526  x0 = 0; y0 = 0; z0 = 0; x1 = _width - 1; y1 = _height-1; z1 = _depth-1;
37527  oldw = disp.width(); oldh = disp.height();
37528  reset_view = false;
37529  }
37530  if (!x0 && !y0 && !z0 && x1==width()-1 && y1==height()-1 && z1==depth()-1) zoom.assign();
37531  else zoom = get_crop(x0,y0,z0,x1,y1,z1);
37532 
37533  const unsigned int
37534  dx = 1 + x1 - x0, dy = 1 + y1 - y0, dz = 1 + z1 - z0,
37535  tw = dx + (dz>1?dz:0), th = dy + (dz>1?dz:0);
37536  if (!disp.is_fullscreen() && resize_disp) {
37537  const unsigned int
37538  ttw = tw*disp.width()/oldw, tth = th*disp.height()/oldh,
37539  dM = cimg::max(ttw,tth), diM = (unsigned int)cimg::max(disp.width(),disp.height()),
37540  imgw = cimg::max(16U,ttw*diM/dM), imgh = cimg::max(16U,tth*diM/dM);
37541  disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
37542  resize_disp = false;
37543  }
37544  oldw = tw; oldh = th;
37545 
37546  bool
37547  go_up = false, go_down = false, go_left = false, go_right = false,
37548  go_inc = false, go_dec = false, go_in = false, go_out = false,
37549  go_in_center = false;
37550  const CImg<T>& visu = zoom?zoom:*this;
37551  const CImg<intT> selection = visu._get_select(disp,0,2,_XYZ,x0,y0,z0,is_first_select);
37552  is_first_select = false;
37553 
37554  if (disp.wheel()) {
37555  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { go_out = !(go_in = disp.wheel()>0); go_in_center = false; }
37556  else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) { go_right = !(go_left = disp.wheel()>0); }
37557  else if (disp.is_keyALT() || disp.is_keyALTGR() || _depth==1) { go_down = !(go_up = disp.wheel()>0); }
37558  disp.set_wheel();
37559  }
37560 
37561  const int
37562  sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
37563  sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
37564  if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
37565  x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1; x0+=sx0; y0+=sy0; z0+=sz0;
37566  if (sx0==sx1 && sy0==sy1 && sz0==sz1) {
37567  if (exit_on_simpleclick && !zoom) break; else reset_view = true;
37568  }
37569  resize_disp = true;
37570  } else switch (key = disp.key()) {
37571 #if cimg_OS!=2
37573 #endif
37574  case 0 : case cimg::keyCTRLLEFT : case cimg::keyPAD5 : case cimg::keySHIFTLEFT :
37575 #if cimg_OS!=2
37576  case cimg::keyALTGR :
37577 #endif
37578  case cimg::keyALT : key = 0; break;
37579  case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) { // Special mode: play stack of frames
37580  const unsigned int
37581  w1 = visu._width*disp.width()/(visu._width+(visu._depth>1?visu._depth:0)),
37582  h1 = visu._height*disp.height()/(visu._height+(visu._depth>1?visu._depth:0));
37583  float frame_timing = 5;
37584  bool is_stopped = false;
37585  disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
37586  for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
37587  if (disp.is_resized()) disp.resize(false);
37588  if (!timer) {
37589  visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
37590  (++_XYZ[2])%=visu._depth;
37591  }
37592  if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
37593  if (disp.wheel()) { frame_timing-=disp.wheel()/3.0f; disp.set_wheel(); }
37594  switch (key = disp.key()) {
37595 #if cimg_OS!=2
37596  case cimg::keyCTRLRIGHT :
37597 #endif
37598  case cimg::keyCTRLLEFT : key = 0; break;
37599  case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
37600  case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
37601  case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
37602  case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
37603  case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true; (_XYZ[2]+=visu._depth-2)%=visu._depth; timer = 0; key = 0; break;
37604  case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37605  disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
37606  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
37607  disp.set_key(key,false); key = 0;
37608  } break;
37609  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37610  disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
37611  } break;
37612  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37613  disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
37614  } break;
37615  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
37616  disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen().set_key(key,false); key = 0;
37617  } break;
37618  }
37619  frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
37620  disp.wait(20);
37621  }
37622  const unsigned int
37623  w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
37624  h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
37625  disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
37626  key = 0;
37627  } break;
37628  case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
37629  case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
37630  case cimg::keyPADSUB : go_out = true; key = 0; break;
37631  case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
37632  case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
37633  case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
37634  case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
37635  case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
37636  case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
37637  case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
37638  case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
37639  case cimg::keyPAGEUP : go_inc = true; key = 0; break;
37640  case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
37641  }
37642  if (go_in) {
37643  const int
37644  mx = go_in_center?disp.width()/2:disp.mouse_x(),
37645  my = go_in_center?disp.height()/2:disp.mouse_y(),
37646  mX = mx*(_width+(_depth>1?_depth:0))/disp.width(),
37647  mY = my*(_height+(_depth>1?_depth:0))/disp.height();
37648  int X = _XYZ[0], Y = _XYZ[1], Z = _XYZ[2];
37649  if (mX<width() && mY<height()) { X = x0 + mX*(1+x1-x0)/_width; Y = y0 + mY*(1+y1-y0)/_height; Z = _XYZ[2]; }
37650  if (mX<width() && mY>=height()) { X = x0 + mX*(1+x1-x0)/_width; Z = z0 + (mY-_height)*(1+z1-z0)/_depth; Y = _XYZ[1]; }
37651  if (mX>=width() && mY<height()) { Y = y0 + mY*(1+y1-y0)/_height; Z = z0 + (mX-_width)*(1+z1-z0)/_depth; X = _XYZ[0]; }
37652  if (x1-x0>4) { x0 = X - 7*(X-x0)/8; x1 = X + 7*(x1-X)/8; }
37653  if (y1-y0>4) { y0 = Y - 7*(Y-y0)/8; y1 = Y + 7*(y1-Y)/8; }
37654  if (z1-z0>4) { z0 = Z - 7*(Z-z0)/8; z1 = Z + 7*(z1-Z)/8; }
37655  }
37656  if (go_out) {
37657  const int
37658  delta_x = (x1-x0)/8, delta_y = (y1-y0)/8, delta_z = (z1-z0)/8,
37659  ndelta_x = delta_x?delta_x:(_width>1?1:0),
37660  ndelta_y = delta_y?delta_y:(_height>1?1:0),
37661  ndelta_z = delta_z?delta_z:(_depth>1?1:0);
37662  x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
37663  x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
37664  if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
37665  if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
37666  if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
37667  if (x1>=width()) { x0-=(x1-width()+1); x1 = width()-1; if (x0<0) x0 = 0; }
37668  if (y1>=height()) { y0-=(y1-height()+1); y1 = height()-1; if (y0<0) y0 = 0; }
37669  if (z1>=depth()) { z0-=(z1-depth()+1); z1 = depth()-1; if (z0<0) z0 = 0; }
37670  }
37671  if (go_left) {
37672  const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0);
37673  if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
37674  else { x1-=x0; x0 = 0; }
37675  }
37676  if (go_right) {
37677  const int delta = (x1-x0)/5, ndelta = delta?delta:(_width>1?1:0);
37678  if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
37679  else { x0+=(width()-1-x1); x1 = width()-1; }
37680  }
37681  if (go_up) {
37682  const int delta = (y1-y0)/5, ndelta = delta?delta:(_height>1?1:0);
37683  if (y0-ndelta>=0) { y0-=ndelta; y1-=ndelta; }
37684  else { y1-=y0; y0 = 0; }
37685  }
37686  if (go_down) {
37687  const int delta = (y1-y0)/5, ndelta = delta?delta:(_height>1?1:0);
37688  if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
37689  else { y0+=(height()-1-y1); y1 = height()-1; }
37690  }
37691  if (go_inc) {
37692  const int delta = (z1-z0)/5, ndelta = delta?delta:(_depth>1?1:0);
37693  if (z0-ndelta>=0) { z0-=ndelta; z1-=ndelta; }
37694  else { z1-=z0; z0 = 0; }
37695  }
37696  if (go_dec) {
37697  const int delta = (z1-z0)/5, ndelta = delta?delta:(_depth>1?1:0);
37698  if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
37699  else { z0+=(depth()-1-z1); z1 = depth()-1; }
37700  }
37701  disp.wait(100);
37702  }
37703  disp.set_key(key);
37704  if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
37705  return *this;
37706  }
37707 
37709 
37728  template<typename tp, typename tf, typename tc, typename to>
37730  const CImg<tp>& vertices,
37731  const CImgList<tf>& primitives,
37732  const CImgList<tc>& colors,
37733  const to& opacities,
37734  const bool centering=true,
37735  const int render_static=4, const int render_motion=1,
37736  const bool is_double_sided=true, const float focale=500,
37737  const float light_x=0, const float light_y=0, const float light_z=-5000,
37738  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
37739  const bool display_axes=true, float *const pose_matrix=0) const {
37740  return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
37741  render_motion,is_double_sided,focale,
37742  light_x,light_y,light_z,specular_lightness,specular_shininess,
37743  display_axes,pose_matrix);
37744  }
37745 
37747  template<typename tp, typename tf, typename tc, typename to>
37748  const CImg<T>& display_object3d(const char *const title,
37749  const CImg<tp>& vertices,
37750  const CImgList<tf>& primitives,
37751  const CImgList<tc>& colors,
37752  const to& opacities,
37753  const bool centering=true,
37754  const int render_static=4, const int render_motion=1,
37755  const bool is_double_sided=true, const float focale=500,
37756  const float light_x=0, const float light_y=0, const float light_z=-5000,
37757  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
37758  const bool display_axes=true, float *const pose_matrix=0) const {
37759  CImgDisplay disp;
37760  return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
37761  render_motion,is_double_sided,focale,
37762  light_x,light_y,light_z,specular_lightness,specular_shininess,
37763  display_axes,pose_matrix);
37764  }
37765 
37767  template<typename tp, typename tf, typename tc>
37769  const CImg<tp>& vertices,
37770  const CImgList<tf>& primitives,
37771  const CImgList<tc>& colors,
37772  const bool centering=true,
37773  const int render_static=4, const int render_motion=1,
37774  const bool is_double_sided=true, const float focale=500,
37775  const float light_x=0, const float light_y=0, const float light_z=-5000,
37776  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
37777  const bool display_axes=true, float *const pose_matrix=0) const {
37778  return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
37779  render_static,render_motion,is_double_sided,focale,
37780  light_x,light_y,light_z,specular_lightness,specular_shininess,
37781  display_axes,pose_matrix);
37782  }
37783 
37785  template<typename tp, typename tf, typename tc>
37786  const CImg<T>& display_object3d(const char *const title,
37787  const CImg<tp>& vertices,
37788  const CImgList<tf>& primitives,
37789  const CImgList<tc>& colors,
37790  const bool centering=true,
37791  const int render_static=4, const int render_motion=1,
37792  const bool is_double_sided=true, const float focale=500,
37793  const float light_x=0, const float light_y=0, const float light_z=-5000,
37794  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
37795  const bool display_axes=true, float *const pose_matrix=0) const {
37796  return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
37797  render_static,render_motion,is_double_sided,focale,
37798  light_x,light_y,light_z,specular_lightness,specular_shininess,
37799  display_axes,pose_matrix);
37800  }
37801 
37803  template<typename tp, typename tf>
37805  const CImg<tp>& vertices,
37806  const CImgList<tf>& primitives,
37807  const bool centering=true,
37808  const int render_static=4, const int render_motion=1,
37809  const bool is_double_sided=true, const float focale=500,
37810  const float light_x=0, const float light_y=0, const float light_z=-5000,
37811  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
37812  const bool display_axes=true, float *const pose_matrix=0) const {
37813  return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
37814  render_static,render_motion,is_double_sided,focale,
37815  light_x,light_y,light_z,specular_lightness,specular_shininess,
37816  display_axes,pose_matrix);
37817  }
37818 
37819 
37821  template<typename tp, typename tf>
37822  const CImg<T>& display_object3d(const char *const title,
37823  const CImg<tp>& vertices,
37824  const CImgList<tf>& primitives,
37825  const bool centering=true,
37826  const int render_static=4, const int render_motion=1,
37827  const bool is_double_sided=true, const float focale=500,
37828  const float light_x=0, const float light_y=0, const float light_z=-5000,
37829  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
37830  const bool display_axes=true, float *const pose_matrix=0) const {
37831  return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
37832  render_static,render_motion,is_double_sided,focale,
37833  light_x,light_y,light_z,specular_lightness,specular_shininess,
37834  display_axes,pose_matrix);
37835  }
37836 
37838  template<typename tp>
37840  const CImg<tp>& vertices,
37841  const bool centering=true,
37842  const int render_static=4, const int render_motion=1,
37843  const bool is_double_sided=true, const float focale=500,
37844  const float light_x=0, const float light_y=0, const float light_z=-5000,
37845  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
37846  const bool display_axes=true, float *const pose_matrix=0) const {
37847  return display_object3d(disp,vertices,CImgList<uintT>(),centering,
37848  render_static,render_motion,is_double_sided,focale,
37849  light_x,light_y,light_z,specular_lightness,specular_shininess,
37850  display_axes,pose_matrix);
37851  }
37852 
37854  template<typename tp>
37855  const CImg<T>& display_object3d(const char *const title,
37856  const CImg<tp>& vertices,
37857  const bool centering=true,
37858  const int render_static=4, const int render_motion=1,
37859  const bool is_double_sided=true, const float focale=500,
37860  const float light_x=0, const float light_y=0, const float light_z=-5000,
37861  const float specular_lightness=0.2f, const float specular_shininess=0.1f,
37862  const bool display_axes=true, float *const pose_matrix=0) const {
37863  return display_object3d(title,vertices,CImgList<uintT>(),centering,
37864  render_static,render_motion,is_double_sided,focale,
37865  light_x,light_y,light_z,specular_lightness,specular_shininess,
37866  display_axes,pose_matrix);
37867  }
37868 
37869  template<typename tp, typename tf, typename tc, typename to>
37870  const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
37871  const CImg<tp>& vertices,
37872  const CImgList<tf>& primitives,
37873  const CImgList<tc>& colors,
37874  const to& opacities,
37875  const bool centering,
37876  const int render_static, const int render_motion,
37877  const bool is_double_sided, const float focale,
37878  const float light_x, const float light_y, const float light_z,
37879  const float specular_lightness, const float specular_shininess,
37880  const bool display_axes, float *const pose_matrix) const {
37881  typedef typename cimg::superset<tp,float>::type tpfloat;
37882 
37883  // Check input arguments
37884  if (is_empty()) {
37885  if (disp) return CImg<T>(disp.width(),disp.height(),1,(colors && colors[0].size()==1)?1:3,0).
37886  _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
37887  render_static,render_motion,is_double_sided,focale,
37888  light_x,light_y,light_z,specular_lightness,specular_shininess,
37889  display_axes,pose_matrix);
37890  else return CImg<T>(1,2,1,1,64,128).resize(cimg_fitscreen(640,480,1),1,(colors && colors[0].size()==1)?1:3,3).
37891  _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
37892  render_static,render_motion,is_double_sided,focale,
37893  light_x,light_y,light_z,specular_lightness,specular_shininess,
37894  display_axes,pose_matrix);
37895  } else { if (disp) disp.resize(*this,false); }
37896  char error_message[1024] = { 0 };
37897  if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
37898  throw CImgArgumentException(_cimg_instance
37899  "display_object3d(): Invalid specified 3d object (%u,%u) (%s).",
37900  cimg_instance,vertices._width,primitives._width,error_message);
37901  if (vertices._width && !primitives) {
37902  CImgList<tf> nprimitives(vertices._width,1,1,1,1);
37903  cimglist_for(nprimitives,l) nprimitives(l,0) = l;
37904  return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
37905  render_static,render_motion,is_double_sided,focale,
37906  light_x,light_y,light_z,specular_lightness,specular_shininess,
37907  display_axes,pose_matrix);
37908  }
37909  if (!disp) {
37910  disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
37911  if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",pixel_type(),vertices._width,primitives._width);
37912  } else if (title) disp.set_title("%s",title);
37913 
37914  // Init 3d objects and compute object statistics
37915  CImg<floatT>
37916  pose,
37917  rotated_vertices(vertices._width,3),
37918  bbox_vertices, rotated_bbox_vertices,
37919  axes_vertices, rotated_axes_vertices,
37920  bbox_opacities, axes_opacities;
37921  CImgList<uintT> bbox_primitives, axes_primitives;
37922  CImgList<tf> reverse_primitives;
37923  CImgList<T> bbox_colors, bbox_colors2, axes_colors;
37924  unsigned int ns_width = 0, ns_height = 0;
37925  int _is_double_sided = (int)is_double_sided;
37926  bool ndisplay_axes = display_axes;
37927  const CImg<T>
37928  background_color(1,1,1,_spectrum,0),
37929  foreground_color(1,1,1,_spectrum,255);
37930  float
37931  Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
37932  xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
37933  ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
37934  zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
37935  const float delta = cimg::max(xM-xm,yM-ym,zM-zm);
37936 
37937  rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
37938  xm,xM,xM,xm,xm,xM,xM,xm,
37939  ym,ym,yM,yM,ym,ym,yM,yM,
37940  zm,zm,zm,zm,zM,zM,zM,zM);
37941  bbox_primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 1,2,6,5, 0,4,7,3, 0,1,5,4, 2,3,7,6);
37942  bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
37943  bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
37944  bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
37945 
37946  rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
37947  0,20,0,0,22,-6,-6,
37948  0,0,20,0,-6,22,-6,
37949  0,0,0,20,0,0,22);
37950  axes_opacities.assign(3,1,1,1,1);
37951  axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
37952  axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
37953 
37954  // Begin user interaction loop
37955  CImg<T> visu0(*this), visu;
37956  CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
37957  bool init_pose = true, clicked = false, redraw = true;
37958  unsigned int key = 0;
37959  int
37960  x0 = 0, y0 = 0, x1 = 0, y1 = 0,
37961  nrender_static = render_static,
37962  nrender_motion = render_motion;
37963  disp.show().flush();
37964 
37965  while (!disp.is_closed() && !key) {
37966 
37967  // Init object pose
37968  if (init_pose) {
37969  const float
37970  ratio = delta>0?(2.0f*cimg::min(disp.width(),disp.height())/(3.0f*delta)):1,
37971  dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
37972  if (centering)
37973  CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
37974  else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
37975  if (pose_matrix) {
37976  CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
37977  pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
37978  pose0(3,3) = pose(3,3) = 1;
37979  (pose0*pose).get_crop(0,0,3,2).move_to(pose);
37980  Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
37981  } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
37982  init_pose = false;
37983  redraw = true;
37984  }
37985 
37986  // Rotate and draw 3d object
37987  if (redraw) {
37988  const float
37989  r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
37990  r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
37991  r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
37992  if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0))
37993  cimg_forX(vertices,l) {
37994  const float x = (float)vertices(l,0), y = (float)vertices(l,1), z = (float)vertices(l,2);
37995  rotated_vertices(l,0) = r00*x + r10*y + r20*z + r30;
37996  rotated_vertices(l,1) = r01*x + r11*y + r21*z + r31;
37997  rotated_vertices(l,2) = r02*x + r12*y + r22*z + r32;
37998  }
37999  else cimg_forX(bbox_vertices,l) {
38000  const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
38001  rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
38002  rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
38003  rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
38004  }
38005 
38006  // Draw object
38007  visu = visu0;
38008  if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
38009  visu.draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
38010  rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
38011  draw_object3d(Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
38012  rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
38013  else visu._draw_object3d((void*)0,(!clicked && nrender_static>0)?zbuffer.fill(0):CImg<tpfloat>::empty(),
38014  Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
38015  rotated_vertices,reverse_primitives?reverse_primitives:primitives,
38016  colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
38017  width()/2.0f+light_x,height()/2.0f+light_y,light_z,specular_lightness,specular_shininess,
38018  sprite_scale);
38019  // Draw axes
38020  if (ndisplay_axes) {
38021  const float
38022  n = (float)std::sqrt(1e-8 + r00*r00 + r01*r01 + r02*r02),
38023  _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
38024  _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
38025  _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
38026  Xaxes = 25, Yaxes = visu._height - 38.0f;
38027  cimg_forX(axes_vertices,l) {
38028  const float
38029  x = axes_vertices(l,0),
38030  y = axes_vertices(l,1),
38031  z = axes_vertices(l,2);
38032  rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
38033  rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
38034  rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
38035  }
38036  axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.0f;
38037  axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.0f;
38038  axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.0f;
38039  visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,axes_colors,axes_opacities,1,false,focale).
38040  draw_text((int)(Xaxes+rotated_axes_vertices(4,0)),
38041  (int)(Yaxes+rotated_axes_vertices(4,1)),
38042  "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
38043  draw_text((int)(Xaxes+rotated_axes_vertices(5,0)),
38044  (int)(Yaxes+rotated_axes_vertices(5,1)),
38045  "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
38046  draw_text((int)(Xaxes+rotated_axes_vertices(6,0)),
38047  (int)(Yaxes+rotated_axes_vertices(6,1)),
38048  "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
38049  }
38050  visu.display(disp);
38051  if (!clicked || nrender_motion==nrender_static) redraw = false;
38052  }
38053 
38054  // Handle user interaction
38055  disp.wait();
38056  if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
38057  redraw = true;
38058  if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
38059  else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
38060  if (disp.button()&1) {
38061  const float
38062  R = 0.45f*cimg::min(disp.width(),disp.height()),
38063  R2 = R*R,
38064  u0 = (float)(x0-disp.width()/2),
38065  v0 = (float)(y0-disp.height()/2),
38066  u1 = (float)(x1-disp.width()/2),
38067  v1 = (float)(y1-disp.height()/2),
38068  n0 = (float)std::sqrt(u0*u0+v0*v0),
38069  n1 = (float)std::sqrt(u1*u1+v1*v1),
38070  nu0 = n0>R?(u0*R/n0):u0,
38071  nv0 = n0>R?(v0*R/n0):v0,
38072  nw0 = (float)std::sqrt(cimg::max(0,R2-nu0*nu0-nv0*nv0)),
38073  nu1 = n1>R?(u1*R/n1):u1,
38074  nv1 = n1>R?(v1*R/n1):v1,
38075  nw1 = (float)std::sqrt(cimg::max(0,R2-nu1*nu1-nv1*nv1)),
38076  u = nv0*nw1-nw0*nv1,
38077  v = nw0*nu1-nu0*nw1,
38078  w = nv0*nu1-nu0*nv1,
38079  n = (float)std::sqrt(u*u+v*v+w*w),
38080  alpha = (float)std::asin(n/R2);
38081  (CImg<floatT>::rotation_matrix(u,v,w,alpha)*pose).move_to(pose);
38082  x0 = x1; y0 = y1;
38083  }
38084  if (disp.button()&2) {
38085  if (focale>0) Zoff-=(y0-y1)*focale/400;
38086  else { const float s = std::exp((y0-y1)/400.0f); pose*=s; sprite_scale*=s; }
38087  x0 = x1; y0 = y1;
38088  }
38089  if (disp.wheel()) {
38090  if (focale>0) Zoff-=disp.wheel()*focale/20;
38091  else { const float s = std::exp(disp.wheel()/20.0f); pose*=s; sprite_scale*=s; }
38092  disp.set_wheel();
38093  }
38094  if (disp.button()&4) { Xoff+=(x1-x0); Yoff+=(y1-y0); x0 = x1; y0 = y1; }
38095  if ((disp.button()&1) && (disp.button()&2)) {
38096  init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
38097  pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
38098  }
38099  } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
38100 
38101  switch (key = disp.key()) {
38102 #if cimg_OS!=2
38103  case cimg::keyCTRLRIGHT :
38104 #endif
38105  case 0 : case cimg::keyCTRLLEFT : key = 0; break;
38106  case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38107  disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
38108  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
38109  _is_resized = true;
38110  disp.set_key(key,false); key = 0;
38111  } break;
38112  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38113  disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
38114  disp.set_key(key,false); key = 0;
38115  } break;
38116  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38117  disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
38118  disp.set_key(key,false); key = 0;
38119  } break;
38120  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38121  if (!ns_width || !ns_height ||
38122  ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
38123  ns_width = disp.screen_width()*3U/4;
38124  ns_height = disp.screen_height()*3U/4;
38125  }
38126  if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
38127  else {
38128  ns_width = (unsigned int)disp.width(); ns_height = disp.height();
38129  disp.resize(disp.screen_width(),disp.screen_height(),false);
38130  }
38131  disp.toggle_fullscreen()._is_resized = true;
38132  disp.set_key(key,false); key = 0;
38133  } break;
38134  case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Switch single/double-sided primitives.
38135  if (--_is_double_sided==-2) _is_double_sided = 1;
38136  if (_is_double_sided>=0) reverse_primitives.assign();
38137  else primitives.get_reverse_object3d().move_to(reverse_primitives);
38138  disp.set_key(key,false); key = 0; redraw = true;
38139  } break;
38140  case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
38141  if (zbuffer) zbuffer.assign();
38142  else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
38143  disp.set_key(key,false); key = 0; redraw = true;
38144  } break;
38145  case cimg::keyA : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3d axes.
38146  ndisplay_axes = !ndisplay_axes;
38147  disp.set_key(key,false); key = 0; redraw = true;
38148  } break;
38149  case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points.
38150  nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
38151  disp.set_key(key,false); key = 0; redraw = true;
38152  } break;
38153  case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines.
38154  nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
38155  disp.set_key(key,false); key = 0; redraw = true;
38156  } break;
38157  case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat.
38158  nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
38159  disp.set_key(key,false); key = 0; redraw = true;
38160  } break;
38161  case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded.
38162  nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
38163  disp.set_key(key,false); key = 0; redraw = true;
38164  } break;
38165  case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to gouraud-shaded.
38166  nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
38167  disp.set_key(key,false); key = 0; redraw = true;
38168  } break;
38169  case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded.
38170  nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
38171  disp.set_key(key,false); key = 0; redraw = true;
38172  } break;
38173  case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
38174  static unsigned int snap_number = 0;
38175  char filename[32] = { 0 };
38176  std::FILE *file;
38177  do {
38178  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
38179  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38180  } while (file);
38181  (+visu).draw_text(0,0," Saving snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
38182  visu.save(filename);
38183  visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38184  disp.set_key(key,false); key = 0;
38185  } break;
38186  case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
38187  static unsigned int snap_number = 0;
38188  char filename[32] = { 0 };
38189  std::FILE *file;
38190  do {
38191  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.off",snap_number++);
38192  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38193  } while (file);
38194  visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp);
38195  vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
38196  visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38197  disp.set_key(key,false); key = 0;
38198  } break;
38199  case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
38200  static unsigned int snap_number = 0;
38201  char filename[32] = { 0 };
38202  std::FILE *file;
38203  do {
38204 #ifdef cimg_use_zlib
38205  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
38206 #else
38207  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
38208 #endif
38209  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38210  } while (file);
38211  visu.draw_text(0,0," Saving object... ",foreground_color._data,background_color._data,1,13).display(disp);
38212  vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).save(filename);
38213  visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38214  disp.set_key(key,false); key = 0;
38215  } break;
38216 #ifdef cimg_use_board
38217  case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
38218  static unsigned int snap_number = 0;
38219  char filename[32] = { 0 };
38220  std::FILE *file;
38221  do {
38222  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.eps",snap_number++);
38223  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38224  } while (file);
38225  visu.draw_text(0,0," Saving EPS snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
38226  LibBoard::Board board;
38227  (+visu)._draw_object3d(&board,zbuffer.fill(0),
38228  Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
38229  rotated_vertices,reverse_primitives?reverse_primitives:primitives,
38230  colors,opacities,clicked?nrender_motion:nrender_static,
38231  _is_double_sided==1,focale,
38232  visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_lightness,specular_shininess,
38233  sprite_scale);
38234  board.saveEPS(filename);
38235  visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38236  disp.set_key(key,false); key = 0;
38237  } break;
38238  case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
38239  static unsigned int snap_number = 0;
38240  char filename[32] = { 0 };
38241  std::FILE *file;
38242  do {
38243  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.svg",snap_number++);
38244  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
38245  } while (file);
38246  visu.draw_text(0,0," Saving SVG snapshot... ",foreground_color._data,background_color._data,1,13).display(disp);
38247  LibBoard::Board board;
38248  (+visu)._draw_object3d(&board,zbuffer.fill(0),
38249  Xoff + visu._width/2.0f,Yoff + visu._height/2.0f,Zoff,
38250  rotated_vertices,reverse_primitives?reverse_primitives:primitives,
38251  colors,opacities,clicked?nrender_motion:nrender_static,
38252  _is_double_sided==1,focale,
38253  visu.width()/2.0f+light_x,visu.height()/2.0f+light_y,light_z,specular_lightness,specular_shininess,
38254  sprite_scale);
38255  board.saveSVG(filename);
38256  visu.draw_text(0,0," Object '%s' saved. ",foreground_color._data,background_color._data,1,13,filename).display(disp);
38257  disp.set_key(key,false); key = 0;
38258  } break;
38259 #endif
38260  }
38261  if (disp.is_resized()) {
38262  disp.resize(false); visu0 = get_resize(disp,1);
38263  if (zbuffer) zbuffer.assign(disp.width(),disp.height());
38264  redraw = true;
38265  }
38266  }
38267  if (pose_matrix) {
38268  std::memcpy(pose_matrix,pose._data,12*sizeof(float));
38269  pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
38270  }
38271  disp.set_button().set_key(key);
38272  return *this;
38273  }
38274 
38276 
38288  const unsigned int plot_type=1, const unsigned int vertex_type=1,
38289  const char *const labelx=0, const double xmin=0, const double xmax=0,
38290  const char *const labely=0, const double ymin=0, const double ymax=0) const {
38291  if (is_empty())
38292  throw CImgInstanceException(_cimg_instance
38293  "display_graph(): Empty instance.",
38294  cimg_instance);
38295  if (!disp) disp.assign(cimg_fitscreen(640,480,1),0,0).set_title("CImg<%s>",pixel_type());
38296  const unsigned long siz = (unsigned long)_width*_height*_depth, siz1 = cimg::max(1U,siz-1);
38297  const unsigned int old_normalization = disp.normalization();
38298  disp.show().flush()._normalization = 0;
38299 
38300  double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
38301  if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
38302  int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
38303 
38304  for (bool reset_view = true, resize_disp = false; !key && !disp.is_closed(); ) {
38305  if (reset_view) { x0 = 0; x1 = width()*height()*depth()-1; y0 = ymin; y1 = ymax; reset_view = false; }
38306  CImg<T> zoom(x1-x0+1,1,1,spectrum());
38307  cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1-x0+1,1,1,1,true);
38308 
38309  if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
38310  if (y0==y1) { --y0; ++y1; }
38311  const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
38312  labelx,
38313  nxmin + x0*(nxmax-nxmin)/siz1,
38314  nxmin + x1*(nxmax-nxmin)/siz1,
38315  labely,y0,y1);
38316  const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
38317  if (selection[0]>=0) {
38318  if (selection[2]<0) reset_view = true;
38319  else {
38320  x1 = x0 + selection[2]; x0+=selection[0];
38321  if (selection[1]>=0 && selection[3]>=0) {
38322  y0 = y1 - selection[3]*(y1-y0)/(disp.height()-32);
38323  y1-=selection[1]*(y1-y0)/(disp.height()-32);
38324  }
38325  }
38326  } else {
38327  bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
38328  switch (key = disp.key()) {
38329  case cimg::keyHOME : reset_view = resize_disp = true; key = 0; disp.set_key(); break;
38330  case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
38331  case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
38332  case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key(); break;
38333  case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key(); break;
38334  case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
38335  case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
38336  case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
38337  case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
38338  case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
38339  case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
38340  }
38341  if (disp.wheel()) {
38342  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_out = !(go_in = disp.wheel()>0);
38343  else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
38344  else go_up = !(go_down = disp.wheel()<0);
38345  key = 0;
38346  }
38347 
38348  if (go_in) {
38349  const int
38350  xsiz = x1 - x0,
38351  mx = (mouse_x-16)*xsiz/(disp.width()-32),
38352  cx = x0 + (mx<0?0:(mx>=xsiz?xsiz:mx));
38353  if (x1-x0>4) {
38354  x0 = cx - 7*(cx-x0)/8; x1 = cx + 7*(x1-cx)/8;
38355  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
38356  const double
38357  ysiz = y1 - y0,
38358  my = (mouse_y-16)*ysiz/(disp.height()-32),
38359  cy = y1 - (my<0?0:(my>=ysiz?ysiz:my));
38360  y0 = cy - 7*(cy-y0)/8; y1 = cy + 7*(y1-cy)/8;
38361  } else y0 = y1 = 0;
38362  }
38363  }
38364  if (go_out) {
38365  if (x0>0 || x1<(int)siz1) {
38366  const int delta_x = (x1-x0)/8, ndelta_x = delta_x?delta_x:(siz>1?1:0);
38367  const double ndelta_y = (y1-y0)/8;
38368  x0-=ndelta_x; x1+=ndelta_x;
38369  y0-=ndelta_y; y1+=ndelta_y;
38370  if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
38371  if (x1>=(int)siz) { x0-=(x1-siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
38372  }
38373  }
38374  if (go_left) {
38375  const int delta = (x1-x0)/5, ndelta = delta?delta:1;
38376  if (x0-ndelta>=0) { x0-=ndelta; x1-=ndelta; }
38377  else { x1-=x0; x0 = 0; }
38378  go_left = false;
38379  }
38380  if (go_right) {
38381  const int delta = (x1-x0)/5, ndelta = delta?delta:1;
38382  if (x1+ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
38383  else { x0+=(siz1-x1); x1 = siz1; }
38384  go_right = false;
38385  }
38386  if (go_up) {
38387  const double delta = (y1-y0)/10, ndelta = delta?delta:1;
38388  y0+=ndelta; y1+=ndelta;
38389  go_up = false;
38390  }
38391  if (go_down) {
38392  const double delta = (y1-y0)/10, ndelta = delta?delta:1;
38393  y0-=ndelta; y1-=ndelta;
38394  go_down = false;
38395  }
38396  }
38397  }
38398  disp._normalization = old_normalization;
38399  return *this;
38400  }
38401 
38403  const CImg<T>& display_graph(const char *const title=0,
38404  const unsigned int plot_type=1, const unsigned int vertex_type=1,
38405  const char *const labelx=0, const double xmin=0, const double xmax=0,
38406  const char *const labely=0, const double ymin=0, const double ymax=0) const {
38407  if (is_empty())
38408  throw CImgInstanceException(_cimg_instance
38409  "display_graph(): Empty instance.",
38410  cimg_instance);
38411  CImgDisplay disp;
38412  return display_graph(disp.set_title("%s",title),plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax);
38413  }
38414 
38416 
38423  const CImg<T>& save(const char *const filename, const int number=-1) const {
38424  if (!filename)
38425  throw CImgArgumentException(_cimg_instance
38426  "save(): Specified filename is (null).",
38427  cimg_instance);
38428  // Do not test for empty instances, since .cimg format is able to manage empty instances.
38429  const char *const ext = cimg::split_filename(filename);
38430  char nfilename[1024] = { 0 };
38431  const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename;
38432 #ifdef cimg_save_plugin
38433  cimg_save_plugin(fn);
38434 #endif
38435 #ifdef cimg_save_plugin1
38436  cimg_save_plugin1(fn);
38437 #endif
38438 #ifdef cimg_save_plugin2
38439  cimg_save_plugin2(fn);
38440 #endif
38441 #ifdef cimg_save_plugin3
38442  cimg_save_plugin3(fn);
38443 #endif
38444 #ifdef cimg_save_plugin4
38445  cimg_save_plugin4(fn);
38446 #endif
38447 #ifdef cimg_save_plugin5
38448  cimg_save_plugin5(fn);
38449 #endif
38450 #ifdef cimg_save_plugin6
38451  cimg_save_plugin6(fn);
38452 #endif
38453 #ifdef cimg_save_plugin7
38454  cimg_save_plugin7(fn);
38455 #endif
38456 #ifdef cimg_save_plugin8
38457  cimg_save_plugin8(fn);
38458 #endif
38459  // Ascii formats
38460  if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
38461  else if (!cimg::strcasecmp(ext,"dlm") ||
38462  !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
38463  else if (!cimg::strcasecmp(ext,"cpp") ||
38464  !cimg::strcasecmp(ext,"hpp") ||
38465  !cimg::strcasecmp(ext,"h") ||
38466  !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
38467 
38468  // 2d binary formats
38469  else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
38470  else if (!cimg::strcasecmp(ext,"jpg") ||
38471  !cimg::strcasecmp(ext,"jpeg") ||
38472  !cimg::strcasecmp(ext,"jpe") ||
38473  !cimg::strcasecmp(ext,"jfif") ||
38474  !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
38475  else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
38476  else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
38477  else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
38478  else if (!cimg::strcasecmp(ext,"pgm") ||
38479  !cimg::strcasecmp(ext,"ppm") ||
38480  !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
38481  else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
38482  else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
38483  else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
38484  else if (!cimg::strcasecmp(ext,"tif") ||
38485  !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
38486 
38487  // 3d binary formats
38488  else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
38489  else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
38490  else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
38491  else if (!cimg::strcasecmp(ext,"hdr") ||
38492  !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
38493  else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
38494  else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
38495  else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
38496  else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
38497 
38498  // Archive files
38499  else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
38500 
38501  // Image sequences
38502  else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
38503  else if (!cimg::strcasecmp(ext,"avi") ||
38504  !cimg::strcasecmp(ext,"mov") ||
38505  !cimg::strcasecmp(ext,"asf") ||
38506  !cimg::strcasecmp(ext,"divx") ||
38507  !cimg::strcasecmp(ext,"flv") ||
38508  !cimg::strcasecmp(ext,"mpg") ||
38509  !cimg::strcasecmp(ext,"m1v") ||
38510  !cimg::strcasecmp(ext,"m2v") ||
38511  !cimg::strcasecmp(ext,"m4v") ||
38512  !cimg::strcasecmp(ext,"mjp") ||
38513  !cimg::strcasecmp(ext,"mkv") ||
38514  !cimg::strcasecmp(ext,"mpe") ||
38515  !cimg::strcasecmp(ext,"movie") ||
38516  !cimg::strcasecmp(ext,"ogm") ||
38517  !cimg::strcasecmp(ext,"ogg") ||
38518  !cimg::strcasecmp(ext,"qt") ||
38519  !cimg::strcasecmp(ext,"rm") ||
38520  !cimg::strcasecmp(ext,"vob") ||
38521  !cimg::strcasecmp(ext,"wmv") ||
38522  !cimg::strcasecmp(ext,"xvid") ||
38523  !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
38524  return save_other(fn);
38525  }
38526 
38528 
38531  const CImg<T>& save_ascii(const char *const filename) const {
38532  return _save_ascii(0,filename);
38533  }
38534 
38536  const CImg<T>& save_ascii(std::FILE *const file) const {
38537  return _save_ascii(file,0);
38538  }
38539 
38540  const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
38541  if (!file && !filename)
38542  throw CImgArgumentException(_cimg_instance
38543  "save_ascii(): Specified filename is (null).",
38544  cimg_instance);
38545  if (is_empty())
38546  throw CImgInstanceException(_cimg_instance
38547  "save_ascii(): Empty instance, for file '%s'.",
38548  cimg_instance,
38549  filename?filename:"(FILE*)");
38550 
38551  std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
38552  std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
38553  const T* ptrs = _data;
38554  cimg_forYZC(*this,y,z,c) {
38555  cimg_forX(*this,x) std::fprintf(nfile,"%.16g ",(double)*(ptrs++));
38556  std::fputc('\n',nfile);
38557  }
38558  if (!file) cimg::fclose(nfile);
38559  return *this;
38560  }
38561 
38563 
38566  const CImg<T>& save_cpp(const char *const filename) const {
38567  return _save_cpp(0,filename);
38568  }
38569 
38571  const CImg<T>& save_cpp(std::FILE *const file) const {
38572  return _save_cpp(file,0);
38573  }
38574 
38575  const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
38576  if (!file && !filename)
38577  throw CImgArgumentException(_cimg_instance
38578  "save_cpp(): Specified filename is (null).",
38579  cimg_instance);
38580  if (is_empty())
38581  throw CImgInstanceException(_cimg_instance
38582  "save_cpp(): Empty instance, for file '%s'.",
38583  cimg_instance,
38584  filename?filename:"(FILE*)");
38585 
38586  std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
38587  char varname[1024] = { 0 };
38588  if (filename) std::sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname);
38589  if (!*varname) cimg_snprintf(varname,sizeof(varname),"unnamed");
38590  std::fprintf(nfile,
38591  "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
38592  "%s data_%s[] = { \n ",
38593  varname,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname);
38594  for (unsigned long off = 0, siz = size()-1; off<=siz; ++off) {
38595  std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
38596  if (off==siz) std::fprintf(nfile," };\n");
38597  else if (!((off+1)%16)) std::fprintf(nfile,",\n ");
38598  else std::fprintf(nfile,", ");
38599  }
38600  if (!file) cimg::fclose(nfile);
38601  return *this;
38602  }
38603 
38605 
38608  const CImg<T>& save_dlm(const char *const filename) const {
38609  return _save_dlm(0,filename);
38610  }
38611 
38613  const CImg<T>& save_dlm(std::FILE *const file) const {
38614  return _save_dlm(file,0);
38615  }
38616 
38617  const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
38618  if (!file && !filename)
38619  throw CImgArgumentException(_cimg_instance
38620  "save_dlm(): Specified filename is (null).",
38621  cimg_instance);
38622  if (is_empty())
38623  throw CImgInstanceException(_cimg_instance
38624  "save_dlm(): Empty instance, for file '%s'.",
38625  cimg_instance,
38626  filename?filename:"(FILE*)");
38627  if (_depth>1)
38628  cimg::warn(_cimg_instance
38629  "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
38630  cimg_instance,
38631  filename?filename:"(FILE*)");
38632 
38633  if (_spectrum>1)
38634  cimg::warn(_cimg_instance
38635  "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
38636  cimg_instance,
38637  filename?filename:"(FILE*)");
38638 
38639  std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
38640  const T* ptrs = _data;
38641  cimg_forYZC(*this,y,z,c) {
38642  cimg_forX(*this,x) std::fprintf(nfile,"%.16g%s",(double)*(ptrs++),(x==width()-1)?"":",");
38643  std::fputc('\n',nfile);
38644  }
38645  if (!file) cimg::fclose(nfile);
38646  return *this;
38647  }
38648 
38650 
38653  const CImg<T>& save_bmp(const char *const filename) const {
38654  return _save_bmp(0,filename);
38655  }
38656 
38658  const CImg<T>& save_bmp(std::FILE *const file) const {
38659  return _save_bmp(file,0);
38660  }
38661 
38662  const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
38663  if (!file && !filename)
38664  throw CImgArgumentException(_cimg_instance
38665  "save_bmp(): Specified filename is (null).",
38666  cimg_instance);
38667  if (is_empty())
38668  throw CImgInstanceException(_cimg_instance
38669  "save_bmp(): Empty instance, for file '%s'.",
38670  cimg_instance,
38671  filename?filename:"(FILE*)");
38672  if (_depth>1)
38673  cimg::warn(_cimg_instance
38674  "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
38675  cimg_instance,
38676  filename?filename:"(FILE*)");
38677 
38678  if (_spectrum>3)
38679  cimg::warn(_cimg_instance
38680  "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
38681  cimg_instance,
38682  filename?filename:"(FILE*)");
38683 
38684  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38685  unsigned char header[54] = { 0 }, align_buf[4] = { 0 };
38686  const unsigned int
38687  align = (4 - (3*_width)%4)%4,
38688  buf_size = (3*_width + align)*height(),
38689  file_size = 54 + buf_size;
38690  header[0] = 'B'; header[1] = 'M';
38691  header[0x02] = file_size&0xFF;
38692  header[0x03] = (file_size>>8)&0xFF;
38693  header[0x04] = (file_size>>16)&0xFF;
38694  header[0x05] = (file_size>>24)&0xFF;
38695  header[0x0A] = 0x36;
38696  header[0x0E] = 0x28;
38697  header[0x12] = _width&0xFF;
38698  header[0x13] = (_width>>8)&0xFF;
38699  header[0x14] = (_width>>16)&0xFF;
38700  header[0x15] = (_width>>24)&0xFF;
38701  header[0x16] = _height&0xFF;
38702  header[0x17] = (_height>>8)&0xFF;
38703  header[0x18] = (_height>>16)&0xFF;
38704  header[0x19] = (_height>>24)&0xFF;
38705  header[0x1A] = 1;
38706  header[0x1B] = 0;
38707  header[0x1C] = 24;
38708  header[0x1D] = 0;
38709  header[0x22] = buf_size&0xFF;
38710  header[0x23] = (buf_size>>8)&0xFF;
38711  header[0x24] = (buf_size>>16)&0xFF;
38712  header[0x25] = (buf_size>>24)&0xFF;
38713  header[0x27] = 0x1;
38714  header[0x2B] = 0x1;
38715  cimg::fwrite(header,54,nfile);
38716 
38717  const T
38718  *ptr_r = data(0,_height-1,0,0),
38719  *ptr_g = (_spectrum>=2)?data(0,_height-1,0,1):0,
38720  *ptr_b = (_spectrum>=3)?data(0,_height-1,0,2):0;
38721 
38722  switch (_spectrum) {
38723  case 1 : {
38724  cimg_forY(*this,y) {
38725  cimg_forX(*this,x) {
38726  const unsigned char val = (unsigned char)*(ptr_r++);
38727  std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
38728  }
38729  cimg::fwrite(align_buf,align,nfile);
38730  ptr_r-=2*_width;
38731  }
38732  } break;
38733  case 2 : {
38734  cimg_forY(*this,y) {
38735  cimg_forX(*this,x) {
38736  std::fputc(0,nfile);
38737  std::fputc((unsigned char)(*(ptr_g++)),nfile);
38738  std::fputc((unsigned char)(*(ptr_r++)),nfile);
38739  }
38740  cimg::fwrite(align_buf,align,nfile);
38741  ptr_r-=2*_width; ptr_g-=2*_width;
38742  }
38743  } break;
38744  default : {
38745  cimg_forY(*this,y) {
38746  cimg_forX(*this,x) {
38747  std::fputc((unsigned char)(*(ptr_b++)),nfile);
38748  std::fputc((unsigned char)(*(ptr_g++)),nfile);
38749  std::fputc((unsigned char)(*(ptr_r++)),nfile);
38750  }
38751  cimg::fwrite(align_buf,align,nfile);
38752  ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
38753  }
38754  }
38755  }
38756  if (!file) cimg::fclose(nfile);
38757  return *this;
38758  }
38759 
38761 
38765  const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
38766  return _save_jpeg(0,filename,quality);
38767  }
38768 
38770  const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
38771  return _save_jpeg(file,0,quality);
38772  }
38773 
38774  const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
38775  if (!file && !filename)
38776  throw CImgArgumentException(_cimg_instance
38777  "save_jpeg(): Specified filename is (null).",
38778  cimg_instance);
38779  if (is_empty())
38780  throw CImgInstanceException(_cimg_instance
38781  "save_jpeg(): Empty instance, for file '%s'.",
38782  cimg_instance,
38783  filename?filename:"(FILE*)");
38784  if (_depth>1)
38785  cimg::warn(_cimg_instance
38786  "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
38787  cimg_instance,
38788  filename?filename:"(FILE*)");
38789 
38790 #ifndef cimg_use_jpeg
38791  if (!file) return save_other(filename,quality);
38792  else throw CImgIOException(_cimg_instance
38793  "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
38794  cimg_instance);
38795 #else
38796  unsigned int dimbuf = 0;
38797  J_COLOR_SPACE colortype = JCS_RGB;
38798 
38799  switch(_spectrum) {
38800  case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
38801  case 2 : dimbuf = 3; colortype = JCS_RGB; break;
38802  case 3 : dimbuf = 3; colortype = JCS_RGB; break;
38803  default : dimbuf = 4; colortype = JCS_CMYK; break;
38804  }
38805 
38806  // Call libjpeg functions
38807  struct jpeg_compress_struct cinfo;
38808  struct jpeg_error_mgr jerr;
38809  cinfo.err = jpeg_std_error(&jerr);
38810  jpeg_create_compress(&cinfo);
38811  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
38812  jpeg_stdio_dest(&cinfo,nfile);
38813  cinfo.image_width = _width;
38814  cinfo.image_height = _height;
38815  cinfo.input_components = dimbuf;
38816  cinfo.in_color_space = colortype;
38817  jpeg_set_defaults(&cinfo);
38818  jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
38819  jpeg_start_compress(&cinfo,TRUE);
38820 
38821  JSAMPROW row_pointer[1];
38822  CImg<ucharT> buffer((unsigned long)_width*dimbuf);
38823 
38824  while (cinfo.next_scanline < cinfo.image_height) {
38825  unsigned char *ptrd = buffer._data;
38826 
38827  // Fill pixel buffer
38828  switch (_spectrum) {
38829  case 1 : { // Greyscale images
38830  const T *ptr_g = data(0, cinfo.next_scanline);
38831  for(unsigned int b = 0; b < cinfo.image_width; b++)
38832  *(ptrd++) = (unsigned char)*(ptr_g++);
38833  } break;
38834  case 2 : { // RG images
38835  const T *ptr_r = data(0,cinfo.next_scanline,0,0),
38836  *ptr_g = data(0,cinfo.next_scanline,0,1);
38837  for(unsigned int b = 0; b < cinfo.image_width; ++b) {
38838  *(ptrd++) = (unsigned char)*(ptr_r++);
38839  *(ptrd++) = (unsigned char)*(ptr_g++);
38840  *(ptrd++) = 0;
38841  }
38842  } break;
38843  case 3 : { // RGB images
38844  const T *ptr_r = data(0,cinfo.next_scanline,0,0),
38845  *ptr_g = data(0,cinfo.next_scanline,0,1),
38846  *ptr_b = data(0,cinfo.next_scanline,0,2);
38847  for(unsigned int b = 0; b < cinfo.image_width; ++b) {
38848  *(ptrd++) = (unsigned char)*(ptr_r++);
38849  *(ptrd++) = (unsigned char)*(ptr_g++);
38850  *(ptrd++) = (unsigned char)*(ptr_b++);
38851  }
38852  } break;
38853  default : { // CMYK images
38854  const T *ptr_r = data(0,cinfo.next_scanline,0,0),
38855  *ptr_g = data(0,cinfo.next_scanline,0,1),
38856  *ptr_b = data(0,cinfo.next_scanline,0,2),
38857  *ptr_a = data(0,cinfo.next_scanline,0,3);
38858  for(unsigned int b = 0; b < cinfo.image_width; ++b) {
38859  *(ptrd++) = (unsigned char)*(ptr_r++);
38860  *(ptrd++) = (unsigned char)*(ptr_g++);
38861  *(ptrd++) = (unsigned char)*(ptr_b++);
38862  *(ptrd++) = (unsigned char)*(ptr_a++);
38863  }
38864  }
38865  }
38866  *row_pointer = buffer._data;
38867  jpeg_write_scanlines(&cinfo,row_pointer,1);
38868  }
38869  jpeg_finish_compress(&cinfo);
38870  if (!file) cimg::fclose(nfile);
38871  jpeg_destroy_compress(&cinfo);
38872  return *this;
38873 #endif
38874  }
38875 
38877 
38881  const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
38882  if (!filename)
38883  throw CImgArgumentException(_cimg_instance
38884  "save_magick(): Specified filename is (null).",
38885  cimg_instance);
38886  if (is_empty())
38887  throw CImgInstanceException(_cimg_instance
38888  "save_magick(): Empty instance, for file '%s'.",
38889  cimg_instance,
38890  filename);
38891 #ifdef cimg_use_magick
38892  double stmin, stmax = (double)max_min(stmin);
38893  if (_depth>1)
38894  cimg::warn(_cimg_instance
38895  "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
38896  cimg_instance,
38897  filename);
38898 
38899  if (_spectrum>3)
38900  cimg::warn(_cimg_instance
38901  "save_magick(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
38902  cimg_instance,
38903  filename);
38904 
38905  if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
38906  cimg::warn(_cimg_instance
38907  "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
38908  cimg_instance,
38909  filename,stmin,stmax);
38910 
38911  Magick::Image image(Magick::Geometry(_width,_height),"black");
38912  image.type(Magick::TrueColorType);
38913  image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
38914  const T
38915  *ptr_r = data(0,0,0,0),
38916  *ptr_g = _spectrum>1?data(0,0,0,1):0,
38917  *ptr_b = _spectrum>2?data(0,0,0,2):0;
38918  Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
38919  switch (_spectrum) {
38920  case 1 : // Scalar images
38921  for (unsigned long off = (unsigned long)_width*_height; off; --off) {
38922  pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
38923  ++pixels;
38924  }
38925  break;
38926  case 2 : // RG images
38927  for (unsigned long off = (unsigned long)_width*_height; off; --off) {
38928  pixels->red = (Magick::Quantum)*(ptr_r++);
38929  pixels->green = (Magick::Quantum)*(ptr_g++);
38930  pixels->blue = 0; ++pixels;
38931  }
38932  break;
38933  default : // RGB images
38934  for (unsigned long off = (unsigned long)_width*_height; off; --off) {
38935  pixels->red = (Magick::Quantum)*(ptr_r++);
38936  pixels->green = (Magick::Quantum)*(ptr_g++);
38937  pixels->blue = (Magick::Quantum)*(ptr_b++);
38938  ++pixels;
38939  }
38940  }
38941  image.syncPixels();
38942  image.write(filename);
38943 #else
38944  cimg::unused(bytes_per_pixel);
38945  throw CImgIOException(_cimg_instance
38946  "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
38947  cimg_instance,
38948  filename);
38949 #endif
38950  return *this;
38951  }
38952 
38954 
38958  const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
38959  return _save_png(0,filename,bytes_per_pixel);
38960  }
38961 
38963  const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
38964  return _save_png(file,0,bytes_per_pixel);
38965  }
38966 
38967  const CImg<T>& _save_png(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const {
38968  if (!file && !filename)
38969  throw CImgArgumentException(_cimg_instance
38970  "save_png(): Specified filename is (null).",
38971  cimg_instance);
38972  if (is_empty())
38973  throw CImgInstanceException(_cimg_instance
38974  "save_png(): Empty image, for file '%s'.",
38975  cimg_instance,
38976  filename?filename:"(FILE*)");
38977 #ifndef cimg_use_png
38978  cimg::unused(bytes_per_pixel);
38979  if (!file) return save_other(filename);
38980  else throw CImgIOException(_cimg_instance
38981  "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
38982  cimg_instance);
38983 #else
38984  const char *volatile nfilename = filename; // two 'volatile' here to remove a g++ warning due to 'setjmp'.
38985  std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
38986 
38987  double stmin, stmax = (double)max_min(stmin);
38988  if (_depth>1)
38989  cimg::warn(_cimg_instance
38990  "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
38991  cimg_instance,
38992  filename);
38993 
38994  if (_spectrum>4)
38995  cimg::warn(_cimg_instance
38996  "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
38997  cimg_instance,
38998  filename);
38999 
39000  if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
39001  cimg::warn(_cimg_instance
39002  "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
39003  cimg_instance,
39004  filename,stmin,stmax);
39005 
39006  // Setup PNG structures for write
39007  png_voidp user_error_ptr = 0;
39008  png_error_ptr user_error_fn = 0, user_warning_fn = 0;
39009  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn, user_warning_fn);
39010  if(!png_ptr){
39011  if (!file) cimg::fclose(nfile);
39012  throw CImgIOException(_cimg_instance
39013  "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
39014  cimg_instance,
39015  nfilename?nfilename:"(FILE*)");
39016  }
39017  png_infop info_ptr = png_create_info_struct(png_ptr);
39018  if (!info_ptr) {
39019  png_destroy_write_struct(&png_ptr,(png_infopp)0);
39020  if (!file) cimg::fclose(nfile);
39021  throw CImgIOException(_cimg_instance
39022  "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
39023  cimg_instance,
39024  nfilename?nfilename:"(FILE*)");
39025  }
39026  if (setjmp(png_jmpbuf(png_ptr))) {
39027  png_destroy_write_struct(&png_ptr, &info_ptr);
39028  if (!file) cimg::fclose(nfile);
39029  throw CImgIOException(_cimg_instance
39030  "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
39031  cimg_instance,
39032  nfilename?nfilename:"(FILE*)");
39033  }
39034  png_init_io(png_ptr, nfile);
39035  const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
39036  int color_type;
39037  switch (spectrum()) {
39038  case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
39039  case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
39040  case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
39041  default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
39042  }
39043  const int interlace_type = PNG_INTERLACE_NONE;
39044  const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
39045  const int filter_method = PNG_FILTER_TYPE_DEFAULT;
39046  png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
39047  png_write_info(png_ptr,info_ptr);
39048  const int byte_depth = bit_depth>>3;
39049  const int numChan = spectrum()>4?4:spectrum();
39050  const int pixel_bit_depth_flag = numChan * (bit_depth-1);
39051 
39052  // Allocate Memory for Image Save and Fill pixel data
39053  png_bytep *const imgData = new png_byte*[_height];
39054  for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
39055  const T *pC0 = data(0,0,0,0);
39056  switch (pixel_bit_depth_flag) {
39057  case 7 : { // Gray 8-bit
39058  cimg_forY(*this,y) {
39059  unsigned char *ptrd = imgData[y];
39060  cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
39061  }
39062  } break;
39063  case 14 : { // Gray w/ Alpha 8-bit
39064  const T *pC1 = data(0,0,0,1);
39065  cimg_forY(*this,y) {
39066  unsigned char *ptrd = imgData[y];
39067  cimg_forX(*this,x) {
39068  *(ptrd++) = (unsigned char)*(pC0++);
39069  *(ptrd++) = (unsigned char)*(pC1++);
39070  }
39071  }
39072  } break;
39073  case 21 : { // RGB 8-bit
39074  const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
39075  cimg_forY(*this,y) {
39076  unsigned char *ptrd = imgData[y];
39077  cimg_forX(*this,x) {
39078  *(ptrd++) = (unsigned char)*(pC0++);
39079  *(ptrd++) = (unsigned char)*(pC1++);
39080  *(ptrd++) = (unsigned char)*(pC2++);
39081  }
39082  }
39083  } break;
39084  case 28 : { // RGB x/ Alpha 8-bit
39085  const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
39086  cimg_forY(*this,y){
39087  unsigned char *ptrd = imgData[y];
39088  cimg_forX(*this,x){
39089  *(ptrd++) = (unsigned char)*(pC0++);
39090  *(ptrd++) = (unsigned char)*(pC1++);
39091  *(ptrd++) = (unsigned char)*(pC2++);
39092  *(ptrd++) = (unsigned char)*(pC3++);
39093  }
39094  }
39095  } break;
39096  case 15 : { // Gray 16-bit
39097  cimg_forY(*this,y){
39098  unsigned short *ptrd = (unsigned short*)(imgData[y]);
39099  cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
39100  if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
39101  }
39102  } break;
39103  case 30 : { // Gray w/ Alpha 16-bit
39104  const T *pC1 = data(0,0,0,1);
39105  cimg_forY(*this,y){
39106  unsigned short *ptrd = (unsigned short*)(imgData[y]);
39107  cimg_forX(*this,x) {
39108  *(ptrd++) = (unsigned short)*(pC0++);
39109  *(ptrd++) = (unsigned short)*(pC1++);
39110  }
39111  if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
39112  }
39113  } break;
39114  case 45 : { // RGB 16-bit
39115  const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
39116  cimg_forY(*this,y) {
39117  unsigned short *ptrd = (unsigned short*)(imgData[y]);
39118  cimg_forX(*this,x) {
39119  *(ptrd++) = (unsigned short)*(pC0++);
39120  *(ptrd++) = (unsigned short)*(pC1++);
39121  *(ptrd++) = (unsigned short)*(pC2++);
39122  }
39123  if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
39124  }
39125  } break;
39126  case 60 : { // RGB w/ Alpha 16-bit
39127  const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
39128  cimg_forY(*this,y) {
39129  unsigned short *ptrd = (unsigned short*)(imgData[y]);
39130  cimg_forX(*this,x) {
39131  *(ptrd++) = (unsigned short)*(pC0++);
39132  *(ptrd++) = (unsigned short)*(pC1++);
39133  *(ptrd++) = (unsigned short)*(pC2++);
39134  *(ptrd++) = (unsigned short)*(pC3++);
39135  }
39136  if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
39137  }
39138  } break;
39139  default :
39140  if (!file) cimg::fclose(nfile);
39141  throw CImgIOException(_cimg_instance
39142  "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
39143  cimg_instance,
39144  nfilename?nfilename:"(FILE*)");
39145  }
39146  png_write_image(png_ptr,imgData);
39147  png_write_end(png_ptr,info_ptr);
39148  png_destroy_write_struct(&png_ptr, &info_ptr);
39149 
39150  // Deallocate Image Write Memory
39151  cimg_forY(*this,n) delete[] imgData[n];
39152  delete[] imgData;
39153  if (!file) cimg::fclose(nfile);
39154  return *this;
39155 #endif
39156  }
39157 
39159 
39163  const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
39164  return _save_pnm(0,filename,bytes_per_pixel);
39165  }
39166 
39168  const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
39169  return _save_pnm(file,0,bytes_per_pixel);
39170  }
39171 
39172  const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename, const unsigned int bytes_per_pixel=0) const {
39173  if (!file && !filename)
39174  throw CImgArgumentException(_cimg_instance
39175  "save_pnm(): Specified filename is (null).",
39176  cimg_instance);
39177  if (is_empty())
39178  throw CImgInstanceException(_cimg_instance
39179  "save_pnm(): Empty instance, for file '%s'.",
39180  cimg_instance,
39181  filename?filename:"(FILE*)");
39182 
39183  double stmin, stmax = (double)max_min(stmin);
39184  if (_depth>1)
39185  cimg::warn(_cimg_instance
39186  "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
39187  cimg_instance,
39188  filename?filename:"(FILE*)");
39189 
39190  if (_spectrum>3)
39191  cimg::warn(_cimg_instance
39192  "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
39193  cimg_instance,
39194  filename?filename:"(FILE*)");
39195 
39196  if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
39197  cimg::warn(_cimg_instance
39198  "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
39199  cimg_instance,
39200  stmin,stmax,filename?filename:"(FILE*)");
39201 
39202  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39203  const T
39204  *ptr_r = data(0,0,0,0),
39205  *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
39206  *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
39207  const unsigned long buf_size = cimg::min(1024*1024UL,_width*_height*(_spectrum==1?1UL:3UL));
39208 
39209  std::fprintf(nfile,"P%c\n%u %u\n%u\n",
39210  (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
39211 
39212  switch (_spectrum) {
39213  case 1 : { // Scalar image
39214  if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
39215  CImg<ucharT> buf(buf_size);
39216  for (long to_write = (long)_width*_height; to_write>0; ) {
39217  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39218  unsigned char *ptrd = buf._data;
39219  for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
39220  cimg::fwrite(buf._data,N,nfile);
39221  to_write-=N;
39222  }
39223  } else { // Binary PGM 16 bits
39224  CImg<ushortT> buf(buf_size);
39225  for (long to_write = (long)_width*_height; to_write>0; ) {
39226  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39227  unsigned short *ptrd = buf._data;
39228  for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
39229  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39230  cimg::fwrite(buf._data,N,nfile);
39231  to_write-=N;
39232  }
39233  }
39234  } break;
39235  case 2 : { // RG image
39236  if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
39237  CImg<ucharT> buf(buf_size);
39238  for (long to_write = (long)_width*_height; to_write>0; ) {
39239  const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
39240  unsigned char *ptrd = buf._data;
39241  for (unsigned long i = N; i>0; --i) {
39242  *(ptrd++) = (unsigned char)*(ptr_r++);
39243  *(ptrd++) = (unsigned char)*(ptr_g++);
39244  *(ptrd++) = 0;
39245  }
39246  cimg::fwrite(buf._data,3*N,nfile);
39247  to_write-=N;
39248  }
39249  } else { // Binary PPM 16 bits
39250  CImg<ushortT> buf(buf_size);
39251  for (long to_write = (long)_width*_height; to_write>0; ) {
39252  const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
39253  unsigned short *ptrd = buf._data;
39254  for (unsigned long i = N; i>0; --i) {
39255  *(ptrd++) = (unsigned short)*(ptr_r++);
39256  *(ptrd++) = (unsigned short)*(ptr_g++);
39257  *(ptrd++) = 0;
39258  }
39259  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39260  cimg::fwrite(buf._data,3*N,nfile);
39261  to_write-=N;
39262  }
39263  }
39264  } break;
39265  default : { // RGB image
39266  if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
39267  CImg<ucharT> buf(buf_size);
39268  for (long to_write = (long)_width*_height; to_write>0; ) {
39269  const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
39270  unsigned char *ptrd = buf._data;
39271  for (unsigned long i = N; i>0; --i) {
39272  *(ptrd++) = (unsigned char)*(ptr_r++);
39273  *(ptrd++) = (unsigned char)*(ptr_g++);
39274  *(ptrd++) = (unsigned char)*(ptr_b++);
39275  }
39276  cimg::fwrite(buf._data,3*N,nfile);
39277  to_write-=N;
39278  }
39279  } else { // Binary PPM 16 bits
39280  CImg<ushortT> buf(buf_size);
39281  for (long to_write = (long)_width*_height; to_write>0; ) {
39282  const unsigned long N = cimg::min((unsigned long)to_write,buf_size/3);
39283  unsigned short *ptrd = buf._data;
39284  for (unsigned long i = N; i>0; --i) {
39285  *(ptrd++) = (unsigned short)*(ptr_r++);
39286  *(ptrd++) = (unsigned short)*(ptr_g++);
39287  *(ptrd++) = (unsigned short)*(ptr_b++);
39288  }
39289  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39290  cimg::fwrite(buf._data,3*N,nfile);
39291  to_write-=N;
39292  }
39293  }
39294  }
39295  }
39296  if (!file) cimg::fclose(nfile);
39297  return *this;
39298  }
39299 
39301 
39304  const CImg<T>& save_pnk(const char *const filename) const {
39305  return _save_pnk(0,filename);
39306  }
39307 
39309  const CImg<T>& save_pnk(std::FILE *const file) const {
39310  return _save_pnk(file,0);
39311  }
39312 
39313  const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
39314  if (!file && !filename)
39315  throw CImgArgumentException(_cimg_instance
39316  "save_pnk(): Specified filename is (null).",
39317  cimg_instance);
39318  if (is_empty())
39319  throw CImgInstanceException(_cimg_instance
39320  "save_pnk(): Empty instance, for file '%s'.",
39321  cimg_instance,
39322  filename?filename:"(FILE*)");
39323  if (_spectrum>1)
39324  cimg::warn(_cimg_instance
39325  "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
39326  cimg_instance,
39327  filename?filename:"(FILE*)");
39328 
39329  const unsigned long buf_size = cimg::min(1024*1024LU,_width*_height*_depth);
39330  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39331  const T *ptr = data(0,0,0,0);
39332 
39333  if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) _save_pnm(file,filename,0); // Can be saved as regular PNM file.
39334  else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3d.
39335  std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
39336  CImg<ucharT> buf(buf_size);
39337  for (long to_write = (long)_width*_height*_depth; to_write>0; ) {
39338  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39339  unsigned char *ptrd = buf._data;
39340  for (unsigned long i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
39341  cimg::fwrite(buf._data,N,nfile);
39342  to_write-=N;
39343  }
39344  } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3d.
39345  if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
39346  else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
39347  CImg<intT> buf(buf_size);
39348  for (long to_write = (long)_width*_height*_depth; to_write>0; ) {
39349  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39350  int *ptrd = buf._data;
39351  for (unsigned long i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
39352  cimg::fwrite(buf._data,N,nfile);
39353  to_write-=N;
39354  }
39355  } else { // Save as P9: Binary float-valued 3d.
39356  if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
39357  else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
39358  CImg<floatT> buf(buf_size);
39359  for (long to_write = (long)_width*_height*_depth; to_write>0; ) {
39360  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39361  float *ptrd = buf._data;
39362  for (unsigned long i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
39363  cimg::fwrite(buf._data,N,nfile);
39364  to_write-=N;
39365  }
39366  }
39367 
39368  if (!file) cimg::fclose(nfile);
39369  return *this;
39370  }
39371 
39373 
39376  const CImg<T>& save_pfm(const char *const filename) const {
39377  return get_mirror('y')._save_pfm(0,filename);
39378  }
39379 
39381  const CImg<T>& save_pfm(std::FILE *const file) const {
39382  return get_mirror('y')._save_pfm(file,0);
39383  }
39384 
39385  const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
39386  if (!file && !filename)
39387  throw CImgArgumentException(_cimg_instance
39388  "save_pfm(): Specified filename is (null).",
39389  cimg_instance);
39390  if (is_empty())
39391  throw CImgInstanceException(_cimg_instance
39392  "save_pfm(): Empty instance, for file '%s'.",
39393  cimg_instance,
39394  filename?filename:"(FILE*)");
39395  if (_depth>1)
39396  cimg::warn(_cimg_instance
39397  "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
39398  cimg_instance,
39399  filename?filename:"(FILE*)");
39400 
39401  if (_spectrum>3)
39402  cimg::warn(_cimg_instance
39403  "save_pfm(): image instance is multispectral, only the three first channels will be saved in file '%s'.",
39404  cimg_instance,
39405  filename?filename:"(FILE*)");
39406 
39407  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39408  const T
39409  *ptr_r = data(0,0,0,0),
39410  *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
39411  *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
39412  const unsigned int buf_size = cimg::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
39413 
39414  std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
39415  (_spectrum==1?'f':'F'),_width,_height);
39416 
39417  switch (_spectrum) {
39418  case 1 : { // Scalar image
39419  CImg<floatT> buf(buf_size);
39420  for (long to_write = (long)_width*_height; to_write>0; ) {
39421  const unsigned long N = cimg::min((unsigned long)to_write,buf_size);
39422  float *ptrd = buf._data;
39423  for (unsigned long i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
39424  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39425  cimg::fwrite(buf._data,N,nfile);
39426  to_write-=N;
39427  }
39428  } break;
39429  case 2 : { // RG image
39430  CImg<floatT> buf(buf_size);
39431  for (long to_write = (long)_width*_height; to_write>0; ) {
39432  const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
39433  float *ptrd = buf._data;
39434  for (unsigned long i = N; i>0; --i) {
39435  *(ptrd++) = (float)*(ptr_r++);
39436  *(ptrd++) = (float)*(ptr_g++);
39437  *(ptrd++) = 0;
39438  }
39439  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39440  cimg::fwrite(buf._data,3*N,nfile);
39441  to_write-=N;
39442  }
39443  } break;
39444  default : { // RGB image
39445  CImg<floatT> buf(buf_size);
39446  for (long to_write = (long)_width*_height; to_write>0; ) {
39447  const unsigned int N = cimg::min((unsigned int)to_write,buf_size/3);
39448  float *ptrd = buf._data;
39449  for (unsigned long i = N; i>0; --i) {
39450  *(ptrd++) = (float)*(ptr_r++);
39451  *(ptrd++) = (float)*(ptr_g++);
39452  *(ptrd++) = (float)*(ptr_b++);
39453  }
39454  if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
39455  cimg::fwrite(buf._data,3*N,nfile);
39456  to_write-=N;
39457  }
39458  }
39459  }
39460  if (!file) cimg::fclose(nfile);
39461  return *this;
39462  }
39463 
39465 
39468  const CImg<T>& save_rgb(const char *const filename) const {
39469  return _save_rgb(0,filename);
39470  }
39471 
39473  const CImg<T>& save_rgb(std::FILE *const file) const {
39474  return _save_rgb(file,0);
39475  }
39476 
39477  const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
39478  if (!file && !filename)
39479  throw CImgArgumentException(_cimg_instance
39480  "save_rgb(): Specified filename is (null).",
39481  cimg_instance);
39482  if (is_empty())
39483  throw CImgInstanceException(_cimg_instance
39484  "save_rgb(): Empty instance, for file '%s'.",
39485  cimg_instance,
39486  filename?filename:"(FILE*)");
39487  if (_spectrum!=3)
39488  cimg::warn(_cimg_instance
39489  "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
39490  cimg_instance,
39491  filename?filename:"(FILE*)");
39492 
39493  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39494  const unsigned long wh = (unsigned long)_width*_height;
39495  unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
39496  const T
39497  *ptr1 = data(0,0,0,0),
39498  *ptr2 = _spectrum>1?data(0,0,0,1):0,
39499  *ptr3 = _spectrum>2?data(0,0,0,2):0;
39500  switch (_spectrum) {
39501  case 1 : { // Scalar image
39502  for (unsigned long k = 0; k<wh; ++k) {
39503  const unsigned char val = (unsigned char)*(ptr1++);
39504  *(nbuffer++) = val;
39505  *(nbuffer++) = val;
39506  *(nbuffer++) = val;
39507  }
39508  } break;
39509  case 2 : { // RG image
39510  for (unsigned long k = 0; k<wh; ++k) {
39511  *(nbuffer++) = (unsigned char)(*(ptr1++));
39512  *(nbuffer++) = (unsigned char)(*(ptr2++));
39513  *(nbuffer++) = 0;
39514  }
39515  } break;
39516  default : { // RGB image
39517  for (unsigned long k = 0; k<wh; ++k) {
39518  *(nbuffer++) = (unsigned char)(*(ptr1++));
39519  *(nbuffer++) = (unsigned char)(*(ptr2++));
39520  *(nbuffer++) = (unsigned char)(*(ptr3++));
39521  }
39522  }
39523  }
39524  cimg::fwrite(buffer,3*wh,nfile);
39525  if (!file) cimg::fclose(nfile);
39526  delete[] buffer;
39527  return *this;
39528  }
39529 
39531 
39534  const CImg<T>& save_rgba(const char *const filename) const {
39535  return _save_rgba(0,filename);
39536  }
39537 
39539  const CImg<T>& save_rgba(std::FILE *const file) const {
39540  return _save_rgba(file,0);
39541  }
39542 
39543  const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
39544  if (!file && !filename)
39545  throw CImgArgumentException(_cimg_instance
39546  "save_rgba(): Specified filename is (null).",
39547  cimg_instance);
39548  if (is_empty())
39549  throw CImgInstanceException(_cimg_instance
39550  "save_rgba(): Empty instance, for file '%s'.",
39551  cimg_instance,
39552  filename?filename:"(FILE*)");
39553  if (_spectrum!=4)
39554  cimg::warn(_cimg_instance
39555  "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
39556  cimg_instance,
39557  filename?filename:"(FILE*)");
39558 
39559  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39560  const unsigned long wh = (unsigned long)_width*_height;
39561  unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
39562  const T
39563  *ptr1 = data(0,0,0,0),
39564  *ptr2 = _spectrum>1?data(0,0,0,1):0,
39565  *ptr3 = _spectrum>2?data(0,0,0,2):0,
39566  *ptr4 = _spectrum>3?data(0,0,0,3):0;
39567  switch (_spectrum) {
39568  case 1 : { // Scalar images
39569  for (unsigned long k = 0; k<wh; ++k) {
39570  const unsigned char val = (unsigned char)*(ptr1++);
39571  *(nbuffer++) = val;
39572  *(nbuffer++) = val;
39573  *(nbuffer++) = val;
39574  *(nbuffer++) = 255;
39575  }
39576  } break;
39577  case 2 : { // RG images
39578  for (unsigned long k = 0; k<wh; ++k) {
39579  *(nbuffer++) = (unsigned char)(*(ptr1++));
39580  *(nbuffer++) = (unsigned char)(*(ptr2++));
39581  *(nbuffer++) = 0;
39582  *(nbuffer++) = 255;
39583  }
39584  } break;
39585  case 3 : { // RGB images
39586  for (unsigned long k = 0; k<wh; ++k) {
39587  *(nbuffer++) = (unsigned char)(*(ptr1++));
39588  *(nbuffer++) = (unsigned char)(*(ptr2++));
39589  *(nbuffer++) = (unsigned char)(*(ptr3++));
39590  *(nbuffer++) = 255;
39591  }
39592  } break;
39593  default : { // RGBA images
39594  for (unsigned long k = 0; k<wh; ++k) {
39595  *(nbuffer++) = (unsigned char)(*(ptr1++));
39596  *(nbuffer++) = (unsigned char)(*(ptr2++));
39597  *(nbuffer++) = (unsigned char)(*(ptr3++));
39598  *(nbuffer++) = (unsigned char)(*(ptr4++));
39599  }
39600  }
39601  }
39602  cimg::fwrite(buffer,4*wh,nfile);
39603  if (!file) cimg::fclose(nfile);
39604  delete[] buffer;
39605  return *this;
39606  }
39607 
39609 
39621  const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0) const {
39622  if (!filename)
39623  throw CImgArgumentException(_cimg_instance
39624  "save_tiff(): Specified filename is (null).",
39625  cimg_instance);
39626  if (is_empty())
39627  throw CImgInstanceException(_cimg_instance
39628  "save_tiff(): Empty instance, for file '%s'.",
39629  cimg_instance,
39630  filename);
39631 
39632 #ifdef cimg_use_tiff
39633  TIFF *tif = TIFFOpen(filename,"w");
39634  if (tif) {
39635  cimg_forZ(*this,z) get_slice(z)._save_tiff(tif,z,compression_type);
39636  TIFFClose(tif);
39637  } else throw CImgIOException(_cimg_instance
39638  "save_tiff(): Failed to open file '%s' for writing.",
39639  cimg_instance,
39640  filename);
39641 #else
39642  cimg::unused(compression_type);
39643  return save_other(filename);
39644 #endif
39645  return *this;
39646  }
39647 
39648 #ifdef cimg_use_tiff
39649 
39650 #define _cimg_save_tiff(types,typed,compression_type) \
39651  if (!std::strcmp(types,pixel_type())) { const typed foo = (typed)0; return _save_tiff(tif,directory,foo,compression_type); }
39652 
39653  // [internal] Save a plane into a tiff file
39654  template<typename t>
39655  const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const t& pixel_t, const unsigned int compression_type) const {
39656  if (is_empty() || !tif || pixel_t) return *this;
39657  const char *const filename = TIFFFileName(tif);
39658  uint32 rowsperstrip = (uint32)-1;
39659  uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
39660  if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
39661  else photometric = PHOTOMETRIC_MINISBLACK;
39662  TIFFSetDirectory(tif,directory);
39663  TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
39664  TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
39665  TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
39666  TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
39667  if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
39668  else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
39669  else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
39670  TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
39671  TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
39672  TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
39673  TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type?(compression_type-1):COMPRESSION_NONE);
39674  rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
39675  TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
39676  TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
39677  TIFFSetField(tif,TIFFTAG_SOFTWARE,"CImg");
39678  t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
39679  if (buf) {
39680  for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
39681  uint32 nrow = (row + rowsperstrip>_height?_height-row:rowsperstrip);
39682  tstrip_t strip = TIFFComputeStrip(tif,row,0);
39683  tsize_t i = 0;
39684  for (unsigned int rr = 0; rr<nrow; ++rr)
39685  for (unsigned int cc = 0; cc<_width; ++cc)
39686  for (unsigned int vv = 0; vv<spp; ++vv)
39687  buf[i++] = (t)(*this)(cc,row + rr,0,vv);
39688  if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
39689  throw CImgIOException(_cimg_instance
39690  "save_tiff(): Invalid strip writing when saving file '%s'.",
39691  cimg_instance,
39692  filename?filename:"(FILE*)");
39693  }
39694  _TIFFfree(buf);
39695  }
39696  TIFFWriteDirectory(tif);
39697  return (*this);
39698  }
39699 
39700  const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int compression_type) const {
39701  _cimg_save_tiff("bool",unsigned char,compression_type);
39702  _cimg_save_tiff("char",char,compression_type);
39703  _cimg_save_tiff("unsigned char",unsigned char,compression_type);
39704  _cimg_save_tiff("short",short,compression_type);
39705  _cimg_save_tiff("unsigned short",unsigned short,compression_type);
39706  _cimg_save_tiff("int",int,compression_type);
39707  _cimg_save_tiff("unsigned int",unsigned int,compression_type);
39708  _cimg_save_tiff("long",int,compression_type);
39709  _cimg_save_tiff("unsigned long",unsigned int,compression_type);
39710  _cimg_save_tiff("float",float,compression_type);
39711  _cimg_save_tiff("double",float,compression_type);
39712  const char *const filename = TIFFFileName(tif);
39713  throw CImgInstanceException(_cimg_instance
39714  "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
39715  cimg_instance,
39716  pixel_type(),filename?filename:"(FILE*)");
39717  return *this;
39718  }
39719 #endif
39720 
39722 
39726  const CImg<T>& save_minc2(const char *const filename,
39727  const char *const imitate_file=0) const {
39728  if (!filename)
39729  throw CImgArgumentException(_cimg_instance
39730  "save_minc2(): Specified filename is (null).",
39731  cimg_instance);
39732  if (is_empty())
39733  throw CImgInstanceException(_cimg_instance
39734  "save_minc2(): Empty instance, for file '%s'.",
39735  cimg_instance,
39736  filename);
39737 #ifndef cimg_use_minc2
39738  cimg::unused(imitate_file);
39739  return save_other(filename);
39740 #else
39741  minc::minc_1_writer wtr;
39742  if (imitate_file)
39743  wtr.open(filename, imitate_file);
39744  else {
39745  minc::minc_info di;
39746  if(width()) di.push_back(minc::dim_info(width(), width()*0.5, -1, minc::dim_info::DIM_X));
39747  if(height()) di.push_back(minc::dim_info(height(), height()*0.5, -1, minc::dim_info::DIM_Y));
39748  if(depth()) di.push_back(minc::dim_info(depth(), depth()*0.5, -1, minc::dim_info::DIM_Z));
39749  if(spectrum()) di.push_back(minc::dim_info(spectrum(), spectrum()*0.5, -1, minc::dim_info::DIM_TIME));
39750  wtr.open(filename, di, 1, NC_FLOAT, 0);
39751  }
39752  if(typeid(T)==typeid(unsigned char))
39753  wtr.setup_write_byte();
39754  else if(typeid(T)==typeid(int))
39755  wtr.setup_write_int();
39756  else if(typeid(T)==typeid(double))
39757  wtr.setup_write_double();
39758  else
39759  wtr.setup_write_float();
39760  minc::save_standard_volume(wtr, this->_data);
39761  return *this;
39762 #endif
39763  }
39764 
39766 
39770  const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
39771  if (!filename)
39772  throw CImgArgumentException(_cimg_instance
39773  "save_analyze(): Specified filename is (null).",
39774  cimg_instance);
39775  if (is_empty())
39776  throw CImgInstanceException(_cimg_instance
39777  "save_analyze(): Empty instance, for file '%s'.",
39778  cimg_instance,
39779  filename);
39780 
39781  std::FILE *file;
39782  char header[348] = { 0 }, hname[1024] = { 0 }, iname[1024] = { 0 };
39783  const char *const ext = cimg::split_filename(filename);
39784  short datatype=-1;
39785  std::memset(header,0,348);
39786  if (!*ext) { cimg_snprintf(hname,sizeof(hname),"%s.hdr",filename); cimg_snprintf(iname,sizeof(iname),"%s.img",filename); }
39787  if (!cimg::strncasecmp(ext,"hdr",3)) {
39788  std::strcpy(hname,filename); std::strncpy(iname,filename,sizeof(iname)-1); std::sprintf(iname + std::strlen(iname)-3,"img");
39789  }
39790  if (!cimg::strncasecmp(ext,"img",3)) {
39791  std::strcpy(hname,filename); std::strncpy(iname,filename,sizeof(iname)-1); std::sprintf(hname + std::strlen(iname)-3,"hdr");
39792  }
39793  if (!cimg::strncasecmp(ext,"nii",3)) {
39794  std::strncpy(hname,filename,sizeof(hname)-1); *iname = 0;
39795  }
39796  int *const iheader = (int*)header;
39797  *iheader = 348;
39798  std::strcpy(header + 4,"CImg");
39799  std::strcpy(header + 14," ");
39800  ((short*)(header + 36))[0] = 4096;
39801  ((char*)(header + 38))[0] = 114;
39802  ((short*)(header + 40))[0] = 4;
39803  ((short*)(header + 40))[1] = _width;
39804  ((short*)(header + 40))[2] = _height;
39805  ((short*)(header + 40))[3] = _depth;
39806  ((short*)(header + 40))[4] = _spectrum;
39807  if (!cimg::strcasecmp(pixel_type(),"bool")) datatype = 2;
39808  if (!cimg::strcasecmp(pixel_type(),"unsigned char")) datatype = 2;
39809  if (!cimg::strcasecmp(pixel_type(),"char")) datatype = 2;
39810  if (!cimg::strcasecmp(pixel_type(),"unsigned short")) datatype = 4;
39811  if (!cimg::strcasecmp(pixel_type(),"short")) datatype = 4;
39812  if (!cimg::strcasecmp(pixel_type(),"unsigned int")) datatype = 8;
39813  if (!cimg::strcasecmp(pixel_type(),"int")) datatype = 8;
39814  if (!cimg::strcasecmp(pixel_type(),"unsigned long")) datatype = 8;
39815  if (!cimg::strcasecmp(pixel_type(),"long")) datatype = 8;
39816  if (!cimg::strcasecmp(pixel_type(),"float")) datatype = 16;
39817  if (!cimg::strcasecmp(pixel_type(),"double")) datatype = 64;
39818  if (datatype<0)
39819  throw CImgIOException(_cimg_instance
39820  "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
39821  cimg_instance,
39822  pixel_type(),filename);
39823 
39824  ((short*)(header+70))[0] = datatype;
39825  ((short*)(header+72))[0] = sizeof(T);
39826  ((float*)(header+112))[0] = 1;
39827  ((float*)(header+76))[0] = 0;
39828  if (voxel_size) {
39829  ((float*)(header+76))[1] = voxel_size[0];
39830  ((float*)(header+76))[2] = voxel_size[1];
39831  ((float*)(header+76))[3] = voxel_size[2];
39832  } else ((float*)(header+76))[1] = ((float*)(header+76))[2] = ((float*)(header+76))[3] = 1;
39833  file = cimg::fopen(hname,"wb");
39834  cimg::fwrite(header,348,file);
39835  if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
39836  cimg::fwrite(_data,size(),file);
39837  cimg::fclose(file);
39838  return *this;
39839  }
39840 
39842 
39846  const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
39847  CImgList<T>(*this,true).save_cimg(filename,is_compressed);
39848  return *this;
39849  }
39850 
39852  const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
39853  CImgList<T>(*this,true).save_cimg(file,is_compressed);
39854  return *this;
39855  }
39856 
39858 
39866  const CImg<T>& save_cimg(const char *const filename,
39867  const unsigned int n0,
39868  const unsigned int x0, const unsigned int y0,
39869  const unsigned int z0, const unsigned int c0) const {
39870  CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
39871  return *this;
39872  }
39873 
39875  const CImg<T>& save_cimg(std::FILE *const file,
39876  const unsigned int n0,
39877  const unsigned int x0, const unsigned int y0,
39878  const unsigned int z0, const unsigned int c0) const {
39879  CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
39880  return *this;
39881  }
39882 
39884 
39894  static void save_empty_cimg(const char *const filename,
39895  const unsigned int dx, const unsigned int dy=1,
39896  const unsigned int dz=1, const unsigned int dc=1) {
39897  return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
39898  }
39899 
39901 
39905  static void save_empty_cimg(std::FILE *const file,
39906  const unsigned int dx, const unsigned int dy=1,
39907  const unsigned int dz=1, const unsigned int dc=1) {
39908  return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
39909  }
39910 
39912 
39916  const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
39917  return _save_inr(0,filename,voxel_size);
39918  }
39919 
39921  const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
39922  return _save_inr(file,0,voxel_size);
39923  }
39924 
39925  const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
39926  if (!file && !filename)
39927  throw CImgArgumentException(_cimg_instance
39928  "save_inr(): Specified filename is (null).",
39929  cimg_instance);
39930  if (is_empty())
39931  throw CImgInstanceException(_cimg_instance
39932  "save_inr(): Empty instance, for file '%s'.",
39933  cimg_instance,
39934  filename?filename:"(FILE*)");
39935 
39936  int inrpixsize=-1;
39937  const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
39938  if (!cimg::strcasecmp(pixel_type(),"unsigned char")) { inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; }
39939  if (!cimg::strcasecmp(pixel_type(),"char")) { inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1; }
39940  if (!cimg::strcasecmp(pixel_type(),"unsigned short")) { inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2; }
39941  if (!cimg::strcasecmp(pixel_type(),"short")) { inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2; }
39942  if (!cimg::strcasecmp(pixel_type(),"unsigned int")) { inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4; }
39943  if (!cimg::strcasecmp(pixel_type(),"int")) { inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4; }
39944  if (!cimg::strcasecmp(pixel_type(),"float")) { inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4; }
39945  if (!cimg::strcasecmp(pixel_type(),"double")) { inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8; }
39946  if (inrpixsize<=0)
39947  throw CImgIOException(_cimg_instance
39948  "save_inr(): Unsupported pixel type '%s' for file '%s'",
39949  cimg_instance,
39950  pixel_type(),filename?filename:"(FILE*)");
39951 
39952  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
39953  char header[257] = { 0 };
39954  int err = cimg_snprintf(header,sizeof(header),"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",_width,_height,_depth,_spectrum);
39955  if (voxel_size) err+=std::sprintf(header + err,"VX=%g\nVY=%g\nVZ=%g\n",voxel_size[0],voxel_size[1],voxel_size[2]);
39956  err+=std::sprintf(header + err,"TYPE=%s\nCPU=%s\n",inrtype,cimg::endianness()?"sun":"decm");
39957  std::memset(header + err,'\n',252 - err);
39958  std::memcpy(header + 252,"##}\n",4);
39959  cimg::fwrite(header,256,nfile);
39960  cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
39961  if (!file) cimg::fclose(nfile);
39962  return *this;
39963  }
39964 
39966 
39970  const CImg<T>& save_exr(const char *const filename) const {
39971  if (!filename)
39972  throw CImgArgumentException(_cimg_instance
39973  "save_exr(): Specified filename is (null).",
39974  cimg_instance);
39975  if (is_empty())
39976  throw CImgInstanceException(_cimg_instance
39977  "save_exr(): Empty instance, for file '%s'.",
39978  cimg_instance,
39979  filename);
39980  if (_depth>1)
39981  cimg::warn(_cimg_instance
39982  "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
39983  cimg_instance,
39984  filename);
39985 
39986 #ifndef cimg_use_openexr
39987  return save_other(filename);
39988 #else
39989  Imf::Rgba *const ptrd0 = new Imf::Rgba[(unsigned long)_width*_height], *ptrd = ptrd0, rgba;
39990  switch (_spectrum) {
39991  case 1 : { // Grayscale image.
39992  for (const T *ptr_r = data(), *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e;) {
39993  rgba.r = rgba.g = rgba.b = (half)(*(ptr_r++));
39994  rgba.a = (half)1;
39995  *(ptrd++) = rgba;
39996  }
39997  } break;
39998  case 2 : { // RG image.
39999  for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e; ) {
40000  rgba.r = (half)(*(ptr_r++));
40001  rgba.g = (half)(*(ptr_g++));
40002  rgba.b = (half)0;
40003  rgba.a = (half)1;
40004  *(ptrd++) = rgba;
40005  }
40006  } break;
40007  case 3 : { // RGB image.
40008  for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e;) {
40009  rgba.r = (half)(*(ptr_r++));
40010  rgba.g = (half)(*(ptr_g++));
40011  rgba.b = (half)(*(ptr_b++));
40012  rgba.a = (half)1;
40013  *(ptrd++) = rgba;
40014  }
40015  } break;
40016  default : { // RGBA image.
40017  for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3),
40018  *const ptr_e = ptr_r + (unsigned long)_width*_height; ptr_r<ptr_e;) {
40019  rgba.r = (half)(*(ptr_r++));
40020  rgba.g = (half)(*(ptr_g++));
40021  rgba.b = (half)(*(ptr_b++));
40022  rgba.a = (half)(*(ptr_a++));
40023  *(ptrd++) = rgba;
40024  }
40025  } break;
40026  }
40027  Imf::RgbaOutputFile outFile(filename,_width,_height,
40028  _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?Imf::WRITE_RGB:Imf::WRITE_RGBA);
40029  outFile.setFrameBuffer(ptrd0,1,_width);
40030  outFile.writePixels(_height);
40031  delete[] ptrd0;
40032  return *this;
40033 #endif
40034  }
40035 
40037 
40042  const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
40043  return _save_pandore(0,filename,colorspace);
40044  }
40045 
40047 
40051  const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
40052  return _save_pandore(file,0,colorspace);
40053  }
40054 
40055  unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
40056  unsigned int nbdims = 0;
40057  if (id==2 || id==3 || id==4) { dims[0] = 1; dims[1] = _width; nbdims = 2; }
40058  if (id==5 || id==6 || id==7) { dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3; }
40059  if (id==8 || id==9 || id==10) { dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; }
40060  if (id==16 || id==17 || id==18) { dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4; }
40061  if (id==19 || id==20 || id==21) { dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5; }
40062  if (id==22 || id==23 || id==25) { dims[0] = _spectrum; dims[1] = _width; nbdims = 2; }
40063  if (id==26 || id==27 || id==29) { dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3; }
40064  if (id==30 || id==31 || id==33) { dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4; }
40065  return nbdims;
40066  }
40067 
40068  const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename, const unsigned int colorspace) const {
40069 
40070 #define __cimg_save_pandore_case(dtype) \
40071  dtype *buffer = new dtype[size()]; \
40072  const T *ptrs = _data; \
40073  cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
40074  buffer-=size(); \
40075  cimg::fwrite(buffer,size(),nfile); \
40076  delete[] buffer
40077 
40078 #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
40079  if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
40080  unsigned int *iheader = (unsigned int*)(header+12); \
40081  nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
40082  cimg::fwrite(header,36,nfile); \
40083  if (sizeof(unsigned long)==4) { unsigned long ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
40084  else if (sizeof(unsigned int)==4) { unsigned int ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
40085  else if (sizeof(unsigned short)==4) { unsigned short ndims[5] = { 0 }; for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; cimg::fwrite(ndims,nbdims,nfile); } \
40086  else throw CImgIOException(_cimg_instance \
40087  "save_pandore(): Unsupported datatype for file '%s'.",\
40088  cimg_instance, \
40089  filename?filename:"(FILE*)"); \
40090  if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
40091  __cimg_save_pandore_case(unsigned char); \
40092  } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
40093  if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
40094  else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
40095  else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
40096  else throw CImgIOException(_cimg_instance \
40097  "save_pandore(): Unsupported datatype for file '%s'.",\
40098  cimg_instance, \
40099  filename?filename:"(FILE*)"); \
40100  } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
40101  if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
40102  else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
40103  else throw CImgIOException(_cimg_instance \
40104  "save_pandore(): Unsupported datatype for file '%s'.",\
40105  cimg_instance, \
40106  filename?filename:"(FILE*)"); \
40107  } \
40108  saved = true; \
40109  }
40110 
40111  if (!file && !filename)
40112  throw CImgArgumentException(_cimg_instance
40113  "save_pandore(): Specified filename is (null).",
40114  cimg_instance);
40115  if (is_empty())
40116  throw CImgInstanceException(_cimg_instance
40117  "save_pandore(): Empty instance, for file '%s'.",
40118  cimg_instance,
40119  filename?filename:"(FILE*)");
40120 
40121  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
40122  unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
40123  0,0,0,0,'C','I','m','g',0,0,0,0,0,'N','o',' ','d','a','t','e',0,0,0,0 };
40124  unsigned int nbdims, dims[5] = { 0 };
40125  bool saved = false;
40126  _cimg_save_pandore_case(1,1,1,"unsigned char",2);
40127  _cimg_save_pandore_case(1,1,1,"char",3);
40128  _cimg_save_pandore_case(1,1,1,"short",3);
40129  _cimg_save_pandore_case(1,1,1,"unsigned short",3);
40130  _cimg_save_pandore_case(1,1,1,"unsigned int",3);
40131  _cimg_save_pandore_case(1,1,1,"int",3);
40132  _cimg_save_pandore_case(1,1,1,"unsigned long",4);
40133  _cimg_save_pandore_case(1,1,1,"long",3);
40134  _cimg_save_pandore_case(1,1,1,"float",4);
40135  _cimg_save_pandore_case(1,1,1,"double",4);
40136 
40137  _cimg_save_pandore_case(0,1,1,"unsigned char",5);
40138  _cimg_save_pandore_case(0,1,1,"char",6);
40139  _cimg_save_pandore_case(0,1,1,"short",6);
40140  _cimg_save_pandore_case(0,1,1,"unsigned short",6);
40141  _cimg_save_pandore_case(0,1,1,"unsigned int",6);
40142  _cimg_save_pandore_case(0,1,1,"int",6);
40143  _cimg_save_pandore_case(0,1,1,"unsigned long",7);
40144  _cimg_save_pandore_case(0,1,1,"long",6);
40145  _cimg_save_pandore_case(0,1,1,"float",7);
40146  _cimg_save_pandore_case(0,1,1,"double",7);
40147 
40148  _cimg_save_pandore_case(0,0,1,"unsigned char",8);
40149  _cimg_save_pandore_case(0,0,1,"char",9);
40150  _cimg_save_pandore_case(0,0,1,"short",9);
40151  _cimg_save_pandore_case(0,0,1,"unsigned short",9);
40152  _cimg_save_pandore_case(0,0,1,"unsigned int",9);
40153  _cimg_save_pandore_case(0,0,1,"int",9);
40154  _cimg_save_pandore_case(0,0,1,"unsigned long",10);
40155  _cimg_save_pandore_case(0,0,1,"long",9);
40156  _cimg_save_pandore_case(0,0,1,"float",10);
40157  _cimg_save_pandore_case(0,0,1,"double",10);
40158 
40159  _cimg_save_pandore_case(0,1,3,"unsigned char",16);
40160  _cimg_save_pandore_case(0,1,3,"char",17);
40161  _cimg_save_pandore_case(0,1,3,"short",17);
40162  _cimg_save_pandore_case(0,1,3,"unsigned short",17);
40163  _cimg_save_pandore_case(0,1,3,"unsigned int",17);
40164  _cimg_save_pandore_case(0,1,3,"int",17);
40165  _cimg_save_pandore_case(0,1,3,"unsigned long",18);
40166  _cimg_save_pandore_case(0,1,3,"long",17);
40167  _cimg_save_pandore_case(0,1,3,"float",18);
40168  _cimg_save_pandore_case(0,1,3,"double",18);
40169 
40170  _cimg_save_pandore_case(0,0,3,"unsigned char",19);
40171  _cimg_save_pandore_case(0,0,3,"char",20);
40172  _cimg_save_pandore_case(0,0,3,"short",20);
40173  _cimg_save_pandore_case(0,0,3,"unsigned short",20);
40174  _cimg_save_pandore_case(0,0,3,"unsigned int",20);
40175  _cimg_save_pandore_case(0,0,3,"int",20);
40176  _cimg_save_pandore_case(0,0,3,"unsigned long",21);
40177  _cimg_save_pandore_case(0,0,3,"long",20);
40178  _cimg_save_pandore_case(0,0,3,"float",21);
40179  _cimg_save_pandore_case(0,0,3,"double",21);
40180 
40181  _cimg_save_pandore_case(1,1,0,"unsigned char",22);
40182  _cimg_save_pandore_case(1,1,0,"char",23);
40183  _cimg_save_pandore_case(1,1,0,"short",23);
40184  _cimg_save_pandore_case(1,1,0,"unsigned short",23);
40185  _cimg_save_pandore_case(1,1,0,"unsigned int",23);
40186  _cimg_save_pandore_case(1,1,0,"int",23);
40187  _cimg_save_pandore_case(1,1,0,"unsigned long",25);
40188  _cimg_save_pandore_case(1,1,0,"long",23);
40189  _cimg_save_pandore_case(1,1,0,"float",25);
40190  _cimg_save_pandore_case(1,1,0,"double",25);
40191 
40192  _cimg_save_pandore_case(0,1,0,"unsigned char",26);
40193  _cimg_save_pandore_case(0,1,0,"char",27);
40194  _cimg_save_pandore_case(0,1,0,"short",27);
40195  _cimg_save_pandore_case(0,1,0,"unsigned short",27);
40196  _cimg_save_pandore_case(0,1,0,"unsigned int",27);
40197  _cimg_save_pandore_case(0,1,0,"int",27);
40198  _cimg_save_pandore_case(0,1,0,"unsigned long",29);
40199  _cimg_save_pandore_case(0,1,0,"long",27);
40200  _cimg_save_pandore_case(0,1,0,"float",29);
40201  _cimg_save_pandore_case(0,1,0,"double",29);
40202 
40203  _cimg_save_pandore_case(0,0,0,"unsigned char",30);
40204  _cimg_save_pandore_case(0,0,0,"char",31);
40205  _cimg_save_pandore_case(0,0,0,"short",31);
40206  _cimg_save_pandore_case(0,0,0,"unsigned short",31);
40207  _cimg_save_pandore_case(0,0,0,"unsigned int",31);
40208  _cimg_save_pandore_case(0,0,0,"int",31);
40209  _cimg_save_pandore_case(0,0,0,"unsigned long",33);
40210  _cimg_save_pandore_case(0,0,0,"long",31);
40211  _cimg_save_pandore_case(0,0,0,"float",33);
40212  _cimg_save_pandore_case(0,0,0,"double",33);
40213 
40214  if (!file) cimg::fclose(nfile);
40215  return *this;
40216  }
40217 
40219 
40225  const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
40226  return _save_raw(0,filename,is_multiplexed);
40227  }
40228 
40230 
40234  const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
40235  return _save_raw(file,0,is_multiplexed);
40236  }
40237 
40238  const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
40239  if (!file && !filename)
40240  throw CImgArgumentException(_cimg_instance
40241  "save_raw(): Specified filename is (null).",
40242  cimg_instance);
40243  if (is_empty())
40244  throw CImgInstanceException(_cimg_instance
40245  "save_raw(): empty instance, for file '%s'.",
40246  cimg_instance,
40247  filename?filename:"(FILE*)");
40248 
40249  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
40250  if (!is_multiplexed) cimg::fwrite(_data,size(),nfile);
40251  else {
40252  CImg<T> buf(_spectrum);
40253  cimg_forXYZ(*this,x,y,z) {
40254  cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
40255  cimg::fwrite(buf._data,_spectrum,nfile);
40256  }
40257  }
40258  if (!file) cimg::fclose(nfile);
40259  return *this;
40260  }
40261 
40263 
40273  const CImg<T>& save_ffmpeg(const char *const filename, const unsigned int fps=25, const unsigned int bitrate=2048) const {
40274  if (!filename)
40275  throw CImgArgumentException(_cimg_instance
40276  "save_ffmpeg(): Specified filename is (null).",
40277  cimg_instance);
40278  if (is_empty())
40279  throw CImgInstanceException(_cimg_instance
40280  "save_ffmpeg(): Empty instance, for file '%s'.",
40281  cimg_instance,
40282  filename);
40283  if (!fps)
40284  throw CImgArgumentException(_cimg_instance
40285  "save_ffmpeg(): Invalid specified framerate 0, for file '%s'.",
40286  cimg_instance,
40287  filename);
40288 #ifndef cimg_use_ffmpeg
40289  return save_ffmpeg_external(filename,0,fps,bitrate);
40290 #else
40291  CImgList<T> list;
40292  get_split('z').move_to(list);
40293  list.save_ffmpeg(filename,fps,bitrate);
40294 #endif
40295  return *this;
40296  }
40297 
40299 
40304  const CImg<T>& save_yuv(const char *const filename, const bool is_rgb=true) const {
40305  get_split('z').save_yuv(filename,is_rgb);
40306  return *this;
40307  }
40308 
40310 
40314  const CImg<T>& save_yuv(std::FILE *const file, const bool is_rgb=true) const {
40315  get_split('z').save_yuv(file,is_rgb);
40316  return *this;
40317  }
40318 
40320 
40330  template<typename tf, typename tc>
40331  const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
40332  const char *const filename) const {
40333  return _save_off(primitives,colors,0,filename);
40334  }
40335 
40337 
40341  template<typename tf, typename tc>
40342  const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
40343  std::FILE *const file) const {
40344  return _save_off(primitives,colors,file,0);
40345  }
40346 
40347  template<typename tf, typename tc>
40348  const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
40349  std::FILE *const file, const char *const filename) const {
40350  if (!file && !filename)
40351  throw CImgArgumentException(_cimg_instance
40352  "save_off(): Specified filename is (null).",
40353  cimg_instance);
40354  if (is_empty())
40355  throw CImgInstanceException(_cimg_instance
40356  "save_off(): Empty instance, for file '%s'.",
40357  cimg_instance,
40358  filename?filename:"(FILE*)");
40359 
40360  CImgList<T> opacities;
40361  char error_message[1024] = { 0 };
40362  if (!is_object3d(primitives,colors,opacities,true,error_message))
40363  throw CImgInstanceException(_cimg_instance
40364  "save_off(): Invalid specified 3d object, for file '%s' (%s).",
40365  cimg_instance,
40366  filename?filename:"(FILE*)",error_message);
40367 
40368  const CImg<tc> default_color(1,3,1,1,200);
40369  std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
40370  unsigned int supported_primitives = 0;
40371  cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
40372  std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
40373  cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",(float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
40374  cimglist_for(primitives,l) {
40375  const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
40376  const unsigned int psiz = primitives[l].size(), csiz = color.size();
40377  const float r = color[0]/255.0f, g = (csiz>1?color[1]:r)/255.0f, b = (csiz>2?color[2]:g)/255.0f;
40378  switch (psiz) {
40379  case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",(unsigned int)primitives(l,0),r,g,b); break;
40380  case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
40381  case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
40382  (unsigned int)primitives(l,1),r,g,b); break;
40383  case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
40384  (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
40385  case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
40386  case 6 : {
40387  const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
40388  const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
40389  std::fprintf(nfile,"2 %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
40390  } break;
40391  case 9 : {
40392  const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
40393  const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
40394  std::fprintf(nfile,"3 %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
40395  (unsigned int)primitives(l,1),rt,gt,bt);
40396  } break;
40397  case 12 : {
40398  const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
40399  const float rt = color.atXY(xt,yt,0)/255.0f, gt = (csiz>1?color.atXY(xt,yt,1):r)/255.0f, bt = (csiz>2?color.atXY(xt,yt,2):g)/255.0f;
40400  std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",(unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
40401  (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
40402  } break;
40403  }
40404  }
40405  if (!file) cimg::fclose(nfile);
40406  return *this;
40407  }
40408 
40410 
40420  const CImg<T>& save_ffmpeg_external(const char *const filename, const char *const codec=0,
40421  const unsigned int fps=25, const unsigned int bitrate=2048) const {
40422  if (!filename)
40423  throw CImgArgumentException(_cimg_instance
40424  "save_ffmpeg_external(): Specified filename is (null).",
40425  cimg_instance);
40426  if (is_empty())
40427  throw CImgInstanceException(_cimg_instance
40428  "save_ffmpeg_external(): Empty instance, for file '%s'.",
40429  cimg_instance,
40430  filename);
40431  CImgList<T> list;
40432  get_split('z').move_to(list);
40433  list.save_ffmpeg_external(filename,codec,fps,bitrate);
40434  return *this;
40435  }
40436 
40438 
40443  const CImg<T>& save_gzip_external(const char *const filename) const {
40444  if (!filename)
40445  throw CImgArgumentException(_cimg_instance
40446  "save_gzip_external(): Specified filename is (null).",
40447  cimg_instance);
40448  if (is_empty())
40449  throw CImgInstanceException(_cimg_instance
40450  "save_gzip_external(): Empty instance, for file '%s'.",
40451  cimg_instance,
40452  filename);
40453 
40454  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
40455  const char
40456  *ext = cimg::split_filename(filename,body),
40457  *ext2 = cimg::split_filename(body,0);
40458  std::FILE *file;
40459  do {
40460  if (!cimg::strcasecmp(ext,"gz")) {
40461  if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
40462  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
40463  } else {
40464  if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
40465  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
40466  }
40467  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40468  } while (file);
40469  save(filetmp);
40470  cimg_snprintf(command,sizeof(command),"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename);
40471  cimg::system(command);
40472  file = std::fopen(filename,"rb");
40473  if (!file)
40474  throw CImgIOException(_cimg_instance
40475  "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
40476  cimg_instance,
40477  filename);
40478 
40479  else cimg::fclose(file);
40480  std::remove(filetmp);
40481  return *this;
40482  }
40483 
40485 
40491  const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
40492  if (!filename)
40493  throw CImgArgumentException(_cimg_instance
40494  "save_graphicsmagick_external(): Specified filename is (null).",
40495  cimg_instance);
40496  if (is_empty())
40497  throw CImgInstanceException(_cimg_instance
40498  "save_graphicsmagick_external(): Empty instance, for file '%s'.",
40499  cimg_instance,
40500  filename);
40501 
40502  char command[1024] = { 0 }, filetmp[512] = { 0 };
40503  std::FILE *file;
40504  do {
40505  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm");
40506  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40507  } while (file);
40508  save_pnm(filetmp);
40509  cimg_snprintf(command,sizeof(command),"%s convert -quality %u \"%s\" \"%s\"",cimg::graphicsmagick_path(),quality,filetmp,filename);
40510  cimg::system(command);
40511  file = std::fopen(filename,"rb");
40512  if (!file)
40513  throw CImgIOException(_cimg_instance
40514  "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
40515  cimg_instance,
40516  filename);
40517 
40518  if (file) cimg::fclose(file);
40519  std::remove(filetmp);
40520  return *this;
40521  }
40522 
40524 
40530  const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
40531  if (!filename)
40532  throw CImgArgumentException(_cimg_instance
40533  "save_imagemagick_external(): Specified filename is (null).",
40534  cimg_instance);
40535  if (is_empty())
40536  throw CImgInstanceException(_cimg_instance
40537  "save_imagemagick_external(): Empty instance, for file '%s'.",
40538  cimg_instance,
40539  filename);
40540 
40541  char command[1024] = { 0 }, filetmp[512] = { 0 };
40542  std::FILE *file;
40543  do {
40544  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),_spectrum==1?"pgm":"ppm");
40545  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40546  } while (file);
40547  save_pnm(filetmp);
40548  cimg_snprintf(command,sizeof(command),"%s -quality %u \"%s\" \"%s\"",cimg::imagemagick_path(),quality,filetmp,filename);
40549  cimg::system(command);
40550  file = std::fopen(filename,"rb");
40551  if (!file)
40552  throw CImgIOException(_cimg_instance
40553  "save_imagemagick_external(): Failed to save file '%s' with external command 'convert'.",
40554  cimg_instance,
40555  filename);
40556 
40557  if (file) cimg::fclose(file);
40558  std::remove(filetmp);
40559  return *this;
40560  }
40561 
40563 
40568  const CImg<T>& save_medcon_external(const char *const filename) const {
40569  if (!filename)
40570  throw CImgArgumentException(_cimg_instance
40571  "save_medcon_external(): Specified filename is (null).",
40572  cimg_instance);
40573  if (is_empty())
40574  throw CImgInstanceException(_cimg_instance
40575  "save_medcon_external(): Empty instance, for file '%s'.",
40576  cimg_instance,
40577  filename);
40578 
40579  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
40580  std::FILE *file;
40581  do {
40582  cimg_snprintf(filetmp,sizeof(filetmp),"%s.hdr",cimg::filenamerand());
40583  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
40584  } while (file);
40585  save_analyze(filetmp);
40586  cimg_snprintf(command,sizeof(command),"%s -w -c dicom -o %s -f %s",cimg::medcon_path(),filename,filetmp);
40587  cimg::system(command);
40588  std::remove(filetmp);
40589  cimg::split_filename(filetmp,body);
40590  cimg_snprintf(filetmp,sizeof(filetmp),"%s.img",body);
40591  std::remove(filetmp);
40592 
40593  file = std::fopen(filename,"rb");
40594  if (!file) {
40595  cimg_snprintf(command,sizeof(command),"m000-%s",filename);
40596  file = std::fopen(command,"rb");
40597  if (!file) {
40598  cimg::fclose(cimg::fopen(filename,"r"));
40599  throw CImgIOException(_cimg_instance
40600  "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
40601  cimg_instance,
40602  filename);
40603  }
40604  }
40605  cimg::fclose(file);
40606  std::rename(command,filename);
40607  return *this;
40608  }
40609 
40610  // Save image for non natively supported formats.
40621  const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
40622  if (!filename)
40623  throw CImgArgumentException(_cimg_instance
40624  "save_other(): Specified filename is (null).",
40625  cimg_instance);
40626  if (is_empty())
40627  throw CImgInstanceException(_cimg_instance
40628  "save_other(): Empty instance, for file '%s'.",
40629  cimg_instance,
40630  filename);
40631 
40632  const unsigned int omode = cimg::exception_mode();
40633  bool is_saved = true;
40634  cimg::exception_mode() = 0;
40635  try { save_magick(filename); }
40636  catch (CImgException&) {
40637  try { save_imagemagick_external(filename,quality); }
40638  catch (CImgException&) {
40639  try { save_graphicsmagick_external(filename,quality); }
40640  catch (CImgException&) {
40641  is_saved = false;
40642  }
40643  }
40644  }
40645  cimg::exception_mode() = omode;
40646  if (!is_saved)
40647  throw CImgIOException(_cimg_instance
40648  "save_other(): Failed to save file '%s'. Format is not natively supported, and no external commands succeeded.",
40649  cimg_instance,
40650  filename);
40651  return *this;
40652  }
40653 
40654  // [internal] Return a 40x38 color logo of a 'danger' item.
40655  static CImg<T> _logo40x38() {
40656  static bool first_time = true;
40657  static CImg<T> res(40,38,1,3);
40658  if (first_time) {
40659  const unsigned char *ptrs = cimg::logo40x38;
40660  T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
40661  for (unsigned long off = 0; off<(unsigned long)res._width*res._height;) {
40662  const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
40663  for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
40664  }
40665  first_time = false;
40666  }
40667  return res;
40668  }
40669 
40671  };
40672 
40673  /*
40674  #-----------------------------------------
40675  #
40676  #
40677  #
40678  # Definition of the CImgList<T> structure
40679  #
40680  #
40681  #
40682  #------------------------------------------
40683  */
40685  template<typename T>
40686  struct CImgList {
40687  unsigned int _width, _allocated_width;
40688  CImg<T> *_data;
40689 
40691 
40704  typedef CImg<T>* iterator;
40705 
40707 
40712  typedef const CImg<T>* const_iterator;
40713 
40715 
40722  typedef T value_type;
40723 
40724  // Define common T-dependant types.
40725  typedef typename cimg::superset<T,bool>::type Tbool;
40726  typedef typename cimg::superset<T,unsigned char>::type Tuchar;
40727  typedef typename cimg::superset<T,char>::type Tchar;
40728  typedef typename cimg::superset<T,unsigned short>::type Tushort;
40729  typedef typename cimg::superset<T,short>::type Tshort;
40730  typedef typename cimg::superset<T,unsigned int>::type Tuint;
40731  typedef typename cimg::superset<T,int>::type Tint;
40732  typedef typename cimg::superset<T,unsigned long>::type Tulong;
40733  typedef typename cimg::superset<T,long>::type Tlong;
40734  typedef typename cimg::superset<T,float>::type Tfloat;
40735  typedef typename cimg::superset<T,double>::type Tdouble;
40736  typedef typename cimg::last<T,bool>::type boolT;
40737  typedef typename cimg::last<T,unsigned char>::type ucharT;
40738  typedef typename cimg::last<T,char>::type charT;
40739  typedef typename cimg::last<T,unsigned short>::type ushortT;
40740  typedef typename cimg::last<T,short>::type shortT;
40741  typedef typename cimg::last<T,unsigned int>::type uintT;
40742  typedef typename cimg::last<T,int>::type intT;
40743  typedef typename cimg::last<T,unsigned long>::type ulongT;
40744  typedef typename cimg::last<T,long>::type longT;
40745  typedef typename cimg::last<T,float>::type floatT;
40746  typedef typename cimg::last<T,double>::type doubleT;
40747 
40749  //---------------------------
40750  //
40752 
40753  //---------------------------
40754 #ifdef cimglist_plugin
40755 #include cimglist_plugin
40756 #endif
40757 #ifdef cimglist_plugin1
40758 #include cimglist_plugin1
40759 #endif
40760 #ifdef cimglist_plugin2
40761 #include cimglist_plugin2
40762 #endif
40763 #ifdef cimglist_plugin3
40764 #include cimglist_plugin3
40765 #endif
40766 #ifdef cimglist_plugin4
40767 #include cimglist_plugin4
40768 #endif
40769 #ifdef cimglist_plugin5
40770 #include cimglist_plugin5
40771 #endif
40772 #ifdef cimglist_plugin6
40773 #include cimglist_plugin6
40774 #endif
40775 #ifdef cimglist_plugin7
40776 #include cimglist_plugin7
40777 #endif
40778 #ifdef cimglist_plugin8
40779 #include cimglist_plugin8
40780 #endif
40781 
40783  //--------------------------------------------------------
40784  //
40786 
40787  //--------------------------------------------------------
40788 
40790 
40797  delete[] _data;
40798  }
40799 
40801 
40808  _width(0),_allocated_width(0),_data(0) {}
40809 
40811 
40817  explicit CImgList(const unsigned int n):_width(n) {
40818  if (n) _data = new CImg<T>[_allocated_width = cimg::max(16UL,cimg::nearest_pow2(n))];
40819  else { _allocated_width = 0; _data = 0; }
40820  }
40821 
40823 
40831  CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
40832  const unsigned int depth=1, const unsigned int spectrum=1):
40833  _width(0),_allocated_width(0),_data(0) {
40834  assign(n);
40835  cimglist_apply(*this,assign)(width,height,depth,spectrum);
40836  }
40837 
40839 
40847  CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
40848  const unsigned int depth, const unsigned int spectrum, const T val):
40849  _width(0),_allocated_width(0),_data(0) {
40850  assign(n);
40851  cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
40852  }
40853 
40855 
40865  CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
40866  const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
40867  _width(0),_allocated_width(0),_data(0) {
40868 #define _CImgList_stdarg(t) { \
40869  assign(n,width,height,depth,spectrum); \
40870  const unsigned long siz = (unsigned long)width*height*depth*spectrum, nsiz = siz*n; \
40871  T *ptrd = _data->_data; \
40872  va_list ap; \
40873  va_start(ap,val1); \
40874  for (unsigned long l = 0, s = 0, i = 0; i<nsiz; ++i) { \
40875  *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
40876  if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
40877  } \
40878  va_end(ap); \
40879  }
40880  _CImgList_stdarg(int);
40881  }
40882 
40884 
40894  CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
40895  const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
40896  _width(0),_allocated_width(0),_data(0) {
40897  _CImgList_stdarg(double);
40898  }
40899 
40901 
40906  template<typename t>
40907  CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
40908  _width(0),_allocated_width(0),_data(0) {
40909  assign(n);
40910  cimglist_apply(*this,assign)(img,is_shared);
40911  }
40912 
40914 
40918  template<typename t>
40919  explicit CImgList(const CImg<t>& img, const bool is_shared=false):
40920  _width(0),_allocated_width(0),_data(0) {
40921  assign(1);
40922  _data[0].assign(img,is_shared);
40923  }
40924 
40926 
40931  template<typename t1, typename t2>
40932  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
40933  _width(0),_allocated_width(0),_data(0) {
40934  assign(2);
40935  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
40936  }
40937 
40939 
40945  template<typename t1, typename t2, typename t3>
40946  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
40947  _width(0),_allocated_width(0),_data(0) {
40948  assign(3);
40949  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
40950  }
40951 
40953 
40960  template<typename t1, typename t2, typename t3, typename t4>
40961  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4, const bool is_shared=false):
40962  _width(0),_allocated_width(0),_data(0) {
40963  assign(4);
40964  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
40965  }
40966 
40968 
40976  template<typename t1, typename t2, typename t3, typename t4, typename t5>
40977  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
40978  const CImg<t5>& img5, const bool is_shared=false):
40979  _width(0),_allocated_width(0),_data(0) {
40980  assign(5);
40981  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
40982  _data[4].assign(img5,is_shared);
40983  }
40984 
40986 
40995  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
40996  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
40997  const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
40998  _width(0),_allocated_width(0),_data(0) {
40999  assign(6);
41000  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41001  _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
41002  }
41003 
41005 
41015  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
41016  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41017  const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
41018  _width(0),_allocated_width(0),_data(0) {
41019  assign(7);
41020  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41021  _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared);
41022  }
41023 
41025 
41036  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
41037  CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41038  const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8, const bool is_shared=false):
41039  _width(0),_allocated_width(0),_data(0) {
41040  assign(8);
41041  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41042  _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
41043  }
41044 
41046 
41050  template<typename t>
41051  CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
41052  assign(list._width);
41053  cimglist_for(*this,l) _data[l].assign(list[l],false);
41054  }
41055 
41057  CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
41058  assign(list._width);
41059  cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
41060  }
41061 
41063 
41067  template<typename t>
41068  CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
41069  assign(list._width);
41070  cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
41071  }
41072 
41074 
41077  explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
41078  assign(filename);
41079  }
41080 
41082 
41086  explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
41087  assign(disp);
41088  }
41089 
41091 
41095  CImgList<T> res(_width);
41096  cimglist_for(*this,l) res[l].assign(_data[l],true);
41097  return res;
41098  }
41099 
41101  const CImgList<T> get_shared() const {
41102  CImgList<T> res(_width);
41103  cimglist_for(*this,l) res[l].assign(_data[l],true);
41104  return res;
41105  }
41106 
41108 
41112  delete[] _data;
41113  _width = _allocated_width = 0;
41114  _data = 0;
41115  return *this;
41116  }
41117 
41119 
41124  return assign();
41125  }
41126 
41128 
41131  CImgList<T>& assign(const unsigned int n) {
41132  if (!n) return assign();
41133  if (_allocated_width<n || _allocated_width>(n<<2)) {
41134  delete[] _data;
41135  _data = new CImg<T>[_allocated_width=cimg::max(16UL,cimg::nearest_pow2(n))];
41136  }
41137  _width = n;
41138  return *this;
41139  }
41140 
41142 
41145  CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
41146  const unsigned int depth=1, const unsigned int spectrum=1) {
41147  assign(n);
41148  cimglist_apply(*this,assign)(width,height,depth,spectrum);
41149  return *this;
41150  }
41151 
41153 
41156  CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
41157  const unsigned int depth, const unsigned int spectrum, const T val) {
41158  assign(n);
41159  cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
41160  return *this;
41161  }
41162 
41164 
41167  CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
41168  const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
41169  _CImgList_stdarg(int);
41170  return *this;
41171  }
41172 
41174 
41177  CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
41178  const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...) {
41179  _CImgList_stdarg(double);
41180  return *this;
41181  }
41182 
41184 
41187  template<typename t>
41188  CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
41189  assign(n);
41190  cimglist_apply(*this,assign)(img,is_shared);
41191  return *this;
41192  }
41193 
41195 
41198  template<typename t>
41199  CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
41200  assign(1);
41201  _data[0].assign(img,is_shared);
41202  return *this;
41203  }
41204 
41206 
41209  template<typename t1, typename t2>
41210  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
41211  assign(2);
41212  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
41213  return *this;
41214  }
41215 
41217 
41220  template<typename t1, typename t2, typename t3>
41221  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
41222  assign(3);
41223  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
41224  return *this;
41225  }
41226 
41228 
41231  template<typename t1, typename t2, typename t3, typename t4>
41232  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41233  const bool is_shared=false) {
41234  assign(4);
41235  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41236  return *this;
41237  }
41238 
41240 
41243  template<typename t1, typename t2, typename t3, typename t4, typename t5>
41244  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41245  const CImg<t5>& img5, const bool is_shared=false) {
41246  assign(5);
41247  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41248  _data[4].assign(img5,is_shared);
41249  return *this;
41250  }
41251 
41253 
41256  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
41257  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41258  const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
41259  assign(6);
41260  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41261  _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
41262  return *this;
41263  }
41264 
41266 
41269  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
41270  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41271  const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
41272  assign(7);
41273  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41274  _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared);
41275  return *this;
41276  }
41277 
41279 
41282  template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
41283  CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
41284  const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
41285  const bool is_shared=false) {
41286  assign(8);
41287  _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared); _data[3].assign(img4,is_shared);
41288  _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared); _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
41289  return *this;
41290  }
41291 
41293 
41296  template<typename t>
41297  CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
41299  assign(list._width);
41300  cimglist_for(*this,l) _data[l].assign(list[l],false);
41301  return *this;
41302  }
41303 
41305  CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
41306  if (this==&list) return *this;
41307  CImgList<T> res(list._width);
41308  cimglist_for(res,l) res[l].assign(list[l],is_shared);
41309  return res.move_to(*this);
41310  }
41311 
41313 
41316  CImgList<T>& assign(const char *const filename) {
41317  return load(filename);
41318  }
41319 
41321 
41325  return assign(CImg<T>(disp));
41326  }
41327 
41329 
41333  template<typename t>
41335  list.assign(_width);
41336  bool is_one_shared_element = false;
41337  cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
41338  if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
41339  else cimglist_for(*this,l) _data[l].move_to(list[l]);
41340  assign();
41341  return list;
41342  }
41343 
41345 
41351  template<typename t>
41352  CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
41353  if (is_empty()) return list;
41354  const unsigned int npos = pos>list._width?list._width:pos;
41355  list.insert(_width,npos);
41356  bool is_one_shared_element = false;
41357  cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
41358  if (is_one_shared_element) cimglist_for(*this,l) list[npos+l].assign(_data[l]);
41359  else cimglist_for(*this,l) _data[l].move_to(list[npos+l]);
41360  assign();
41361  return list;
41362  }
41363 
41365 
41370  cimg::swap(_width,list._width);
41371  cimg::swap(_allocated_width,list._allocated_width);
41372  cimg::swap(_data,list._data);
41373  return list;
41374  }
41375 
41377 
41383  static CImgList<T>& empty() {
41384  static CImgList<T> _empty;
41385  return _empty.assign();
41386  }
41387 
41389  //------------------------------------------
41390  //
41392 
41393  //------------------------------------------
41394 
41396 
41399  CImg<T>& operator()(const unsigned int pos) {
41400 #if cimg_verbosity>=3
41401  if (pos>=_width) {
41402  cimg::warn(_cimglist_instance
41403  "operator(): Invalid image request, at position [%u].",
41404  cimglist_instance,
41405  pos);
41406  return *_data;
41407  }
41408 #endif
41409  return _data[pos];
41410  }
41411 
41413 
41416  const CImg<T>& operator()(const unsigned int pos) const {
41417  return const_cast<CImgList<T>*>(this)->operator()(pos);
41418  }
41419 
41421 
41429  T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
41430  const unsigned int z=0, const unsigned int c=0) {
41431  return (*this)[pos](x,y,z,c);
41432  }
41433 
41435  const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
41436  const unsigned int z=0, const unsigned int c=0) const {
41437  return (*this)[pos](x,y,z,c);
41438  }
41439 
41441 
41444  operator CImg<T>*() {
41445  return _data;
41446  }
41447 
41449  operator const CImg<T>*() const {
41450  return _data;
41451  }
41452 
41454 
41458  template<typename t>
41460  return assign(img);
41461  }
41462 
41464 
41468  template<typename t>
41470  return assign(list);
41471  }
41472 
41475  return assign(list);
41476  }
41477 
41479 
41482  CImgList<T>& operator=(const char *const filename) {
41483  return assign(filename);
41484  }
41485 
41487 
41491  return assign(disp);
41492  }
41493 
41495 
41499  return CImgList<T>(*this,false);
41500  }
41501 
41503 
41510  template<typename t>
41512  return insert(img);
41513  }
41514 
41516 
41519  template<typename t>
41521  return insert(list);
41522  }
41523 
41525 
41529  CImg<T> operator>(const char axis) const {
41530  return get_append(axis,0);
41531  }
41532 
41534 
41538  CImgList<T> operator<(const char axis) const {
41539  return get_split(axis);
41540  }
41541 
41543  //-------------------------------------
41544  //
41546 
41547  //-------------------------------------
41548 
41550 
41557  static const char* pixel_type() {
41558  return cimg::type<T>::string();
41559  }
41560 
41562 
41565  int width() const {
41566  return (int)_width;
41567  }
41568 
41570 
41573  unsigned int size() const {
41574  return _width;
41575  }
41576 
41578 
41582  return _data;
41583  }
41584 
41586  const CImg<T> *data() const {
41587  return _data;
41588  }
41589 
41591 
41595 #if cimg_verbosity>=3
41596  CImg<T> *data(const unsigned int pos) {
41597  if (pos>=size()) {
41598  cimg::warn(_cimglist_instance
41599  "data(): Invalid pointer request, at position [%u].",
41600  cimglist_instance,
41601  pos);
41602  return _data;
41603  }
41604  return _data + pos;
41605  }
41606 
41607  const CImg<T> *data(const unsigned int l) const {
41608  return const_cast<CImgList<T>*>(this)->data(l);
41609  }
41610 #else
41611  CImg<T> *data(const unsigned int l) {
41612  return _data + l;
41613  }
41614 
41616  const CImg<T> *data(const unsigned int l) const {
41617  return _data + l;
41618  }
41619 #endif
41620 
41622 
41625  return _data;
41626  }
41627 
41630  return _data;
41631  }
41632 
41634 
41637  return _data + _width;
41638  }
41639 
41642  return _data + _width;
41643  }
41644 
41646 
41649  return *_data;
41650  }
41651 
41653  const CImg<T>& front() const {
41654  return *_data;
41655  }
41656 
41658 
41660  const CImg<T>& back() const {
41661  return *(_data + _width - 1);
41662  }
41663 
41666  return *(_data + _width - 1);
41667  }
41668 
41670 
41673  CImg<T>& at(const int pos) {
41674  if (is_empty())
41675  throw CImgInstanceException(_cimglist_instance
41676  "at(): Empty instance.",
41677  cimglist_instance);
41678 
41679  return _data[pos<0?0:pos>=(int)_width?(int)_width-1:pos];
41680  }
41681 
41683 
41692  T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41693  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
41694  }
41695 
41697  T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41698  return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
41699  }
41700 
41702 
41710  T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
41711  if (is_empty())
41712  throw CImgInstanceException(_cimglist_instance
41713  "atNXYZC(): Empty instance.",
41714  cimglist_instance);
41715 
41716  return _atNXYZC(pos,x,y,z,c);
41717  }
41718 
41720  T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
41721  if (is_empty())
41722  throw CImgInstanceException(_cimglist_instance
41723  "atNXYZC(): Empty instance.",
41724  cimglist_instance);
41725 
41726  return _atNXYZC(pos,x,y,z,c);
41727  }
41728 
41729  T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
41730  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
41731  }
41732 
41733  T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
41734  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZC(x,y,z,c);
41735  }
41736 
41738 
41747  T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41748  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
41749  }
41750 
41752  T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41753  return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
41754  }
41755 
41757 
41765  T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
41766  if (is_empty())
41767  throw CImgInstanceException(_cimglist_instance
41768  "atNXYZ(): Empty instance.",
41769  cimglist_instance);
41770 
41771  return _atNXYZ(pos,x,y,z,c);
41772  }
41773 
41775  T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
41776  if (is_empty())
41777  throw CImgInstanceException(_cimglist_instance
41778  "atNXYZ(): Empty instance.",
41779  cimglist_instance);
41780 
41781  return _atNXYZ(pos,x,y,z,c);
41782  }
41783 
41784  T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
41785  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
41786  }
41787 
41788  T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
41789  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXYZ(x,y,z,c);
41790  }
41791 
41793 
41802  T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41803  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
41804  }
41805 
41807  T atNXY(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41808  return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atXY(x,y,z,c,out_value);
41809  }
41810 
41812 
41820  T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
41821  if (is_empty())
41822  throw CImgInstanceException(_cimglist_instance
41823  "atNXY(): Empty instance.",
41824  cimglist_instance);
41825 
41826  return _atNXY(pos,x,y,z,c);
41827  }
41828 
41830  T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
41831  if (is_empty())
41832  throw CImgInstanceException(_cimglist_instance
41833  "atNXY(): Empty instance.",
41834  cimglist_instance);
41835 
41836  return _atNXY(pos,x,y,z,c);
41837  }
41838 
41839  T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
41840  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
41841  }
41842 
41843  T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
41844  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atXY(x,y,z,c);
41845  }
41846 
41848 
41857  T& atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41858  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
41859  }
41860 
41862  T atNX(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41863  return (pos<0 || pos>=(int)_width)?out_value:_data[pos].atX(x,y,z,c,out_value);
41864  }
41865 
41867 
41875  T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
41876  if (is_empty())
41877  throw CImgInstanceException(_cimglist_instance
41878  "atNX(): Empty instance.",
41879  cimglist_instance);
41880 
41881  return _atNX(pos,x,y,z,c);
41882  }
41883 
41885  T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
41886  if (is_empty())
41887  throw CImgInstanceException(_cimglist_instance
41888  "atNX(): Empty instance.",
41889  cimglist_instance);
41890 
41891  return _atNX(pos,x,y,z,c);
41892  }
41893 
41894  T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
41895  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
41896  }
41897 
41898  T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
41899  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)].atX(x,y,z,c);
41900  }
41901 
41903 
41912  T& atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) {
41913  return (pos<0 || pos>=(int)_width)?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
41914  }
41915 
41917  T atN(const int pos, const int x, const int y, const int z, const int c, const T out_value) const {
41918  return (pos<0 || pos>=(int)_width)?out_value:(*this)(pos,x,y,z,c);
41919  }
41920 
41922 
41930  T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
41931  if (is_empty())
41932  throw CImgInstanceException(_cimglist_instance
41933  "atN(): Empty instance.",
41934  cimglist_instance);
41935  return _atN(pos,x,y,z,c);
41936  }
41937 
41939  T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
41940  if (is_empty())
41941  throw CImgInstanceException(_cimglist_instance
41942  "atN(): Empty instance.",
41943  cimglist_instance);
41944  return _atN(pos,x,y,z,c);
41945  }
41946 
41947  T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
41948  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
41949  }
41950 
41951  T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
41952  return _data[pos<0?0:(pos>=(int)_width?(int)_width-1:pos)](x,y,z,c);
41953  }
41954 
41956 
41961  CImg<charT> value_string(const char separator=',', const unsigned int max_size=0) const {
41962  if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
41963  CImgList<charT> items;
41964  for (unsigned int l = 0; l<_width-1; ++l) {
41965  CImg<charT> item = _data[l].value_string(separator,0);
41966  item.back() = separator;
41967  item.move_to(items);
41968  }
41969  _data[_width-1].value_string(separator,0).move_to(items);
41970  CImg<charT> res; (items>'x').move_to(res);
41971  if (max_size) { res.crop(0,max_size); res(max_size) = 0; }
41972  return res;
41973  }
41974 
41976  //-------------------------------------
41977  //
41979 
41980  //-------------------------------------
41981 
41983 
41985  bool is_empty() const {
41986  return (!_data || !_width);
41987  }
41988 
41990 
41993  bool is_sameN(const unsigned int size_n) const {
41994  return _width==size_n;
41995  }
41996 
41998 
42001  template<typename t>
42002  bool is_sameN(const CImgList<t>& list) const {
42003  return is_sameN(list._width);
42004  }
42005 
42006  // Define useful functions to check list dimensions.
42007  // (cannot be documented because macro-generated).
42008 #define _cimglist_def_is_same1(axis) \
42009  bool is_same##axis(const unsigned int val) const { \
42010  bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); return res; \
42011  } \
42012  bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
42013  return is_sameN(n) && is_same##axis(val); \
42014  } \
42015 
42016 #define _cimglist_def_is_same2(axis1,axis2) \
42017  bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
42018  bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); return res; \
42019  } \
42020  bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
42021  return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
42022  } \
42023 
42024 #define _cimglist_def_is_same3(axis1,axis2,axis3) \
42025  bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
42026  bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); return res; \
42027  } \
42028  bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, const unsigned int val2, const unsigned int val3) const { \
42029  return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
42030  } \
42031 
42032 #define _cimglist_def_is_same(axis) \
42033  template<typename t> bool is_same##axis(const CImg<t>& img) const { \
42034  bool res = true; for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); return res; \
42035  } \
42036  template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
42037  const unsigned int lmin = cimg::min(_width,list._width); \
42038  bool res = true; for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); return res; \
42039  } \
42040  template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
42041  return (is_sameN(n) && is_same##axis(img)); \
42042  } \
42043  template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
42044  return (is_sameN(list) && is_same##axis(list)); \
42045  }
42046 
42047  _cimglist_def_is_same(XY)
42048  _cimglist_def_is_same(XZ)
42049  _cimglist_def_is_same(XC)
42050  _cimglist_def_is_same(YZ)
42051  _cimglist_def_is_same(YC)
42052  _cimglist_def_is_same(XYZ)
42053  _cimglist_def_is_same(XYC)
42054  _cimglist_def_is_same(YZC)
42055  _cimglist_def_is_same(XYZC)
42056  _cimglist_def_is_same1(X)
42057  _cimglist_def_is_same1(Y)
42058  _cimglist_def_is_same1(Z)
42059  _cimglist_def_is_same1(C)
42060  _cimglist_def_is_same2(X,Y)
42061  _cimglist_def_is_same2(X,Z)
42062  _cimglist_def_is_same2(X,C)
42063  _cimglist_def_is_same2(Y,Z)
42064  _cimglist_def_is_same2(Y,C)
42065  _cimglist_def_is_same2(Z,C)
42066  _cimglist_def_is_same3(X,Y,Z)
42067  _cimglist_def_is_same3(X,Y,C)
42068  _cimglist_def_is_same3(X,Z,C)
42069  _cimglist_def_is_same3(Y,Z,C)
42070 
42072 
42078  bool is_sameXYZC(const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
42079  bool res = true;
42080  for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
42081  return res;
42082  }
42083 
42085 
42092  bool is_sameNXYZC(const unsigned int n, const unsigned int dx, const unsigned int dy, const unsigned int dz, const unsigned int dc) const {
42093  return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
42094  }
42095 
42097 
42104  bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
42105  if (is_empty()) return false;
42106  return n>=0 && n<(int)_width && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
42107  z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
42108  }
42109 
42111 
42114  bool containsN(const int n) const {
42115  if (is_empty()) return false;
42116  return n>=0 && n<(int)_width;
42117  }
42118 
42120 
42129  template<typename t>
42130  bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
42131  if (is_empty()) return false;
42132  cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
42133  return false;
42134  }
42135 
42137 
42145  template<typename t>
42146  bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
42147  t c;
42148  return contains(pixel,n,x,y,z,c);
42149  }
42150 
42152 
42159  template<typename t>
42160  bool contains(const T& pixel, t& n, t& x, t&y) const {
42161  t z, c;
42162  return contains(pixel,n,x,y,z,c);
42163  }
42164 
42166 
42172  template<typename t>
42173  bool contains(const T& pixel, t& n, t& x) const {
42174  t y, z, c;
42175  return contains(pixel,n,x,y,z,c);
42176  }
42177 
42179 
42184  template<typename t>
42185  bool contains(const T& pixel, t& n) const {
42186  t x, y, z, c;
42187  return contains(pixel,n,x,y,z,c);
42188  }
42189 
42191 
42194  bool contains(const T& pixel) const {
42195  unsigned int n, x, y, z, c;
42196  return contains(pixel,n,x,y,z,c);
42197  }
42198 
42200 
42205  template<typename t>
42206  bool contains(const CImg<T>& img, t& n) const {
42207  if (is_empty()) return false;
42208  const CImg<T> *const ptr = &img;
42209  cimglist_for(*this,i) if (_data+i==ptr) { n = (t)i; return true; }
42210  return false;
42211  }
42212 
42214 
42217  bool contains(const CImg<T>& img) const {
42218  unsigned int n;
42219  return contains(img,n);
42220  }
42221 
42223  //-------------------------------------
42224  //
42226 
42227  //-------------------------------------
42228 
42230 
42232  T& min() {
42233  if (is_empty())
42234  throw CImgInstanceException(_cimglist_instance
42235  "min(): Empty instance.",
42236  cimglist_instance);
42237  T *ptr_min = _data->_data;
42238  T min_value = *ptr_min;
42239  cimglist_for(*this,l) {
42240  const CImg<T>& img = _data[l];
42241  cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
42242  }
42243  return *ptr_min;
42244  }
42245 
42247  const T& min() const {
42248  if (is_empty())
42249  throw CImgInstanceException(_cimglist_instance
42250  "min(): Empty instance.",
42251  cimglist_instance);
42252  const T *ptr_min = _data->_data;
42253  T min_value = *ptr_min;
42254  cimglist_for(*this,l) {
42255  const CImg<T>& img = _data[l];
42256  cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
42257  }
42258  return *ptr_min;
42259  }
42260 
42262 
42264  T& max() {
42265  if (is_empty())
42266  throw CImgInstanceException(_cimglist_instance
42267  "max(): Empty instance.",
42268  cimglist_instance);
42269  T *ptr_max = _data->_data;
42270  T max_value = *ptr_max;
42271  cimglist_for(*this,l) {
42272  const CImg<T>& img = _data[l];
42273  cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
42274  }
42275  return *ptr_max;
42276  }
42277 
42279  const T& max() const {
42280  if (is_empty())
42281  throw CImgInstanceException(_cimglist_instance
42282  "max(): Empty instance.",
42283  cimglist_instance);
42284  const T *ptr_max = _data->_data;
42285  T max_value = *ptr_max;
42286  cimglist_for(*this,l) {
42287  const CImg<T>& img = _data[l];
42288  cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
42289  }
42290  return *ptr_max;
42291  }
42292 
42294 
42297  template<typename t>
42298  T& min_max(t& max_val) {
42299  if (is_empty())
42300  throw CImgInstanceException(_cimglist_instance
42301  "min_max(): Empty instance.",
42302  cimglist_instance);
42303  T *ptr_min = _data->_data;
42304  T min_value = *ptr_min, max_value = min_value;
42305  cimglist_for(*this,l) {
42306  const CImg<T>& img = _data[l];
42307  cimg_for(img,ptrs,T) {
42308  const T val = *ptrs;
42309  if (val<min_value) { min_value = val; ptr_min = ptrs; }
42310  if (val>max_value) max_value = val;
42311  }
42312  }
42313  max_val = (t)max_value;
42314  return *ptr_min;
42315  }
42316 
42318 
42321  template<typename t>
42322  const T& min_max(t& max_val) const {
42323  if (is_empty())
42324  throw CImgInstanceException(_cimglist_instance
42325  "min_max(): Empty instance.",
42326  cimglist_instance);
42327  const T *ptr_min = _data->_data;
42328  T min_value = *ptr_min, max_value = min_value;
42329  cimglist_for(*this,l) {
42330  const CImg<T>& img = _data[l];
42331  cimg_for(img,ptrs,T) {
42332  const T val = *ptrs;
42333  if (val<min_value) { min_value = val; ptr_min = ptrs; }
42334  if (val>max_value) max_value = val;
42335  }
42336  }
42337  max_val = (t)max_value;
42338  return *ptr_min;
42339  }
42340 
42342 
42345  template<typename t>
42346  T& max_min(t& min_val) {
42347  if (is_empty())
42348  throw CImgInstanceException(_cimglist_instance
42349  "max_min(): Empty instance.",
42350  cimglist_instance);
42351  T *ptr_max = _data->_data;
42352  T min_value = *ptr_max, max_value = min_value;
42353  cimglist_for(*this,l) {
42354  const CImg<T>& img = _data[l];
42355  cimg_for(img,ptrs,T) {
42356  const T val = *ptrs;
42357  if (val>max_value) { max_value = val; ptr_max = ptrs; }
42358  if (val<min_value) min_value = val;
42359  }
42360  }
42361  min_val = (t)min_value;
42362  return *ptr_max;
42363  }
42364 
42366  template<typename t>
42367  const T& max_min(t& min_val) const {
42368  if (is_empty())
42369  throw CImgInstanceException(_cimglist_instance
42370  "max_min(): Empty instance.",
42371  cimglist_instance);
42372  const T *ptr_max = _data->_data;
42373  T min_value = *ptr_max, max_value = min_value;
42374  cimglist_for(*this,l) {
42375  const CImg<T>& img = _data[l];
42376  cimg_for(img,ptrs,T) {
42377  const T val = *ptrs;
42378  if (val>max_value) { max_value = val; ptr_max = ptrs; }
42379  if (val<min_value) min_value = val;
42380  }
42381  }
42382  min_val = (t)min_value;
42383  return *ptr_max;
42384  }
42385 
42387  //---------------------------
42388  //
42390 
42391  //---------------------------
42392 
42394 
42399  template<typename t>
42400  CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
42401  const unsigned int npos = pos==~0U?_width:pos;
42402  if (npos>_width)
42403  throw CImgArgumentException(_cimglist_instance
42404  "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.",
42405  cimglist_instance,
42406  img._width,img._height,img._depth,img._spectrum,img._data,npos);
42407  if (is_shared)
42408  throw CImgArgumentException(_cimglist_instance
42409  "insert(): Invalid insertion request of specified shared image CImg<%s>(%u,%u,%u,%u,%p) at position %u "
42410  "(pixel types are different).",
42411  cimglist_instance,
42412  img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
42413 
42414  CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0;
42415  if (!_data) { // Insert new element into empty list.
42416  _data = new_data;
42417  *_data = img;
42418  } else {
42419  if (new_data) { // Insert with re-allocation.
42420  if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
42421  if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
42422  std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
42423  delete[] _data;
42424  _data = new_data;
42425  } else if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos)); // Insert without re-allocation.
42426  _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0;
42427  _data[npos] = img;
42428  }
42429  return *this;
42430  }
42431 
42433  CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
42434  const unsigned int npos = pos==~0U?_width:pos;
42435  if (npos>_width)
42436  throw CImgArgumentException(_cimglist_instance
42437  "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) at position %u.",
42438  cimglist_instance,
42439  img._width,img._height,img._depth,img._spectrum,img._data,npos);
42440  CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):(_allocated_width=16)]:0;
42441  if (!_data) { // Insert new element into empty list.
42442  _data = new_data;
42443  if (is_shared && img) {
42444  _data->_width = img._width; _data->_height = img._height; _data->_depth = img._depth; _data->_spectrum = img._spectrum;
42445  _data->_is_shared = true; _data->_data = img._data;
42446  } else *_data = img;
42447  }
42448  else {
42449  if (new_data) { // Insert with re-allocation.
42450  if (npos) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos);
42451  if (npos!=_width-1) std::memcpy(new_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
42452  if (is_shared && img) {
42453  new_data[npos]._width = img._width; new_data[npos]._height = img._height; new_data[npos]._depth = img._depth;
42454  new_data[npos]._spectrum = img._spectrum; new_data[npos]._is_shared = true; new_data[npos]._data = img._data;
42455  } else {
42456  new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0; new_data[npos]._data = 0;
42457  new_data[npos] = img;
42458  }
42459  std::memset(_data,0,sizeof(CImg<T>)*(_width-1));
42460  delete[] _data;
42461  _data = new_data;
42462  } else { // Insert without re-allocation.
42463  if (npos!=_width-1) std::memmove(_data+npos+1,_data+npos,sizeof(CImg<T>)*(_width-1-npos));
42464  if (is_shared && img) {
42465  _data[npos]._width = img._width; _data[npos]._height = img._height; _data[npos]._depth = img._depth; _data[npos]._spectrum = img._spectrum;
42466  _data[npos]._is_shared = true; _data[npos]._data = img._data;
42467  } else {
42468  _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0; _data[npos]._data = 0;
42469  _data[npos] = img;
42470  }
42471  }
42472  }
42473  return *this;
42474  }
42475 
42477  template<typename t>
42478  CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
42479  return (+*this).insert(img,pos,is_shared);
42480  }
42481 
42483 
42487  CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
42488  CImg<T> empty;
42489  if (!n) return *this;
42490  const unsigned int npos = pos==~0U?_width:pos;
42491  for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
42492  return *this;
42493  }
42494 
42496  CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
42497  return (+*this).insert(n,pos);
42498  }
42499 
42501 
42507  template<typename t>
42508  CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
42509  if (!n) return *this;
42510  const unsigned int npos = pos==~0U?_width:pos;
42511  insert(img,npos,is_shared);
42512  for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos+i,is_shared);
42513  return *this;
42514  }
42515 
42517  template<typename t>
42518  CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
42519  return (+*this).insert(n,img,pos,is_shared);
42520  }
42521 
42523 
42528  template<typename t>
42529  CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
42530  const unsigned int npos = pos==~0U?_width:pos;
42531  if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos+l,is_shared);
42532  else insert(CImgList<T>(list),npos,is_shared);
42533  return *this;
42534  }
42535 
42537  template<typename t>
42538  CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
42539  return (+*this).insert(list,pos,is_shared);
42540  }
42541 
42543 
42549  template<typename t>
42550  CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
42551  if (!n) return *this;
42552  const unsigned int npos = pos==~0U?_width:pos;
42553  for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
42554  return *this;
42555  }
42556 
42558  template<typename t>
42559  CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
42560  return (+*this).insert(n,list,pos,is_shared);
42561  }
42562 
42564 
42568  CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
42569  const unsigned int
42570  npos1 = pos1<pos2?pos1:pos2,
42571  tpos2 = pos1<pos2?pos2:pos1,
42572  npos2 = tpos2<_width?tpos2:_width-1;
42573  if (npos1>=_width)
42574  throw CImgArgumentException(_cimglist_instance
42575  "remove(): Invalid remove request at positions %u->%u.",
42576  cimglist_instance,
42577  npos1,tpos2);
42578  else {
42579  if (tpos2>=_width)
42580  throw CImgArgumentException(_cimglist_instance
42581  "remove(): Invalid remove request at positions %u->%u.",
42582  cimglist_instance,
42583  npos1,tpos2);
42584 
42585  for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
42586  const unsigned int nb = 1 + npos2 - npos1;
42587  if (!(_width-=nb)) return assign();
42588  if (_width>(_allocated_width>>2) || _allocated_width<=16) { // Removing items without reallocation.
42589  if (npos1!=_width) std::memmove(_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width - npos1));
42590  std::memset(_data + _width,0,sizeof(CImg<T>)*nb);
42591  } else { // Removing items with reallocation.
42592  _allocated_width>>=2;
42593  while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
42594  CImg<T> *const new_data = new CImg<T>[_allocated_width];
42595  if (npos1) std::memcpy(new_data,_data,sizeof(CImg<T>)*npos1);
42596  if (npos1!=_width) std::memcpy(new_data+npos1,_data+npos2+1,sizeof(CImg<T>)*(_width-npos1));
42597  if (_width!=_allocated_width) std::memset(new_data+_width,0,sizeof(_allocated_width - _width));
42598  std::memset(_data,0,sizeof(CImg<T>)*(_width+nb));
42599  delete[] _data;
42600  _data = new_data;
42601  }
42602  }
42603  return *this;
42604  }
42605 
42607  CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
42608  return (+*this).remove(pos1,pos2);
42609  }
42610 
42612 
42615  CImgList<T>& remove(const unsigned int pos) {
42616  return remove(pos,pos);
42617  }
42618 
42620  CImgList<T> get_remove(const unsigned int pos) const {
42621  return (+*this).remove(pos);
42622  }
42623 
42625 
42627  CImgList<T>& remove() {
42628  return remove(_width-1);
42629  }
42630 
42633  return (+*this).remove();
42634  }
42635 
42638  for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width-1-l]);
42639  return *this;
42640  }
42641 
42644  return (+*this).reverse();
42645  }
42646 
42648 
42652  CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
42653  return get_images(pos0,pos1).move_to(*this);
42654  }
42655 
42657  CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
42658  if (pos0>pos1 || pos1>=_width)
42659  throw CImgArgumentException(_cimglist_instance
42660  "images(): Specified sub-list indices (%u->%u) are out of bounds.",
42661  cimglist_instance,
42662  pos0,pos1);
42663  CImgList<T> res(pos1-pos0+1);
42664  cimglist_for(res,l) res[l].assign(_data[pos0+l]);
42665  return res;
42666  }
42667 
42669 
42673  CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
42674  if (pos0>pos1 || pos1>=_width)
42675  throw CImgArgumentException(_cimglist_instance
42676  "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
42677  cimglist_instance,
42678  pos0,pos1);
42679  CImgList<T> res(pos1-pos0+1);
42680  cimglist_for(res,l) res[l].assign(_data[pos0+l],true);
42681  return res;
42682  }
42683 
42685  const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
42686  if (pos0>pos1 || pos1>=_width)
42687  throw CImgArgumentException(_cimglist_instance
42688  "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
42689  cimglist_instance,
42690  pos0,pos1);
42691  CImgList<T> res(pos1-pos0+1);
42692  cimglist_for(res,l) res[l].assign(_data[pos0+l],true);
42693  return res;
42694  }
42695 
42697 
42701  CImg<T> get_append(const char axis, const float align=0) const {
42702  if (is_empty()) return CImg<T>();
42703  if (_width==1) return +((*this)[0]);
42704  unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
42705  CImg<T> res;
42706  switch (cimg::uncase(axis)) {
42707  case 'x' : { // Along the X-axis.
42708  cimglist_for(*this,l) {
42709  const CImg<T>& img = (*this)[l];
42710  if (img) { dx+=img._width; dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum); }
42711  }
42712  res.assign(dx,dy,dz,dc,0);
42713  if (res) cimglist_for(*this,l) {
42714  const CImg<T>& img = (*this)[l];
42715  if (img) res.draw_image(pos,
42716  (int)(align*(dy-img._height)),
42717  (int)(align*(dz-img._depth)),
42718  (int)(align*(dc-img._spectrum)),
42719  img);
42720  pos+=img._width;
42721  }
42722  } break;
42723  case 'y' : { // Along the Y-axis.
42724  cimglist_for(*this,l) {
42725  const CImg<T>& img = (*this)[l];
42726  if (img) { dx = cimg::max(dx,img._width); dy+=img._height; dz = cimg::max(dz,img._depth); dc = cimg::max(dc,img._spectrum); }
42727  }
42728  res.assign(dx,dy,dz,dc,0);
42729  if (res) cimglist_for(*this,l) {
42730  const CImg<T>& img = (*this)[l];
42731  if (img) res.draw_image((int)(align*(dx-img._width)),
42732  pos,
42733  (int)(align*(dz-img._depth)),
42734  (int)(align*(dc-img._spectrum)),
42735  img);
42736  pos+=img._height;
42737  }
42738  } break;
42739  case 'z' : { // Along the Z-axis.
42740  cimglist_for(*this,l) {
42741  const CImg<T>& img = (*this)[l];
42742  if (img) { dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz+=img._depth; dc = cimg::max(dc,img._spectrum); }
42743  }
42744  res.assign(dx,dy,dz,dc,0);
42745  if (res) cimglist_for(*this,l) {
42746  const CImg<T>& img = (*this)[l];
42747  if (img) res.draw_image((int)(align*(dx-img._width)),
42748  (int)(align*(dy-img._height)),
42749  pos,
42750  (int)(align*(dc-img._spectrum)),
42751  img);
42752  pos+=img._depth;
42753  }
42754  } break;
42755  default : { // Along the C-axis.
42756  cimglist_for(*this,l) {
42757  const CImg<T>& img = (*this)[l];
42758  if (img) { dx = cimg::max(dx,img._width); dy = cimg::max(dy,img._height); dz = cimg::max(dz,img._depth); dc+=img._spectrum; }
42759  }
42760  res.assign(dx,dy,dz,dc,0);
42761  if (res) cimglist_for(*this,l) {
42762  const CImg<T>& img = (*this)[l];
42763  if (img) res.draw_image((int)(align*(dx-img._width)),
42764  (int)(align*(dy-img._height)),
42765  (int)(align*(dz-img._depth)),
42766  pos,
42767  img);
42768  pos+=img._spectrum;
42769  }
42770  }
42771  }
42772  return res;
42773  }
42774 
42776 
42780  CImgList<T>& split(const char axis, const int nb=0) {
42781  return get_split(axis,nb).move_to(*this);
42782  }
42783 
42785  CImgList<T> get_split(const char axis, const int nb=0) const {
42786  CImgList<T> res;
42787  cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
42788  return res;
42789  }
42790 
42792 
42795  template<typename t>
42797  return insert(img);
42798  }
42799 
42801 
42804  template<typename t>
42806  return insert(img,0);
42807  }
42808 
42810 
42813  template<typename t>
42815  return insert(list);
42816  }
42817 
42819 
42822  template<typename t>
42824  return insert(list,0);
42825  }
42826 
42828 
42831  return remove(_width-1);
42832  }
42833 
42835 
42838  return remove(0);
42839  }
42840 
42842 
42845  CImgList<T>& erase(const iterator iter) {
42846  return remove(iter-_data);
42847  }
42848 
42850  //----------------------------------
42851  //
42853 
42854  //----------------------------------
42855 
42857 
42864  CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
42865  const char axis='x', const float align=0) const {
42866  return _get_select(disp,0,feature_type,axis,align,0,false,false,false);
42867  }
42868 
42870 
42877  CImg<intT> get_select(const char *const title, const bool feature_type=true,
42878  const char axis='x', const float align=0) const {
42879  CImgDisplay disp;
42880  return _get_select(disp,title,feature_type,axis,align,0,false,false,false);
42881  }
42882 
42883  CImg<intT> _get_select(CImgDisplay &disp, const char *const title, const bool feature_type,
42884  const char axis, const float align,
42885  const unsigned int orig, const bool resize_disp,
42886  const bool exit_on_rightbutton, const bool exit_on_wheel) const {
42887  if (is_empty())
42888  throw CImgInstanceException(_cimglist_instance
42889  "select(): Empty instance.",
42890  cimglist_instance);
42891 
42892  // Create image correspondence table and get list dimensions for visualization.
42893  CImgList<uintT> _indices;
42894  unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
42895  cimglist_for(*this,l) if (_data[l]) {
42896  const CImg<T>& img = _data[l];
42897  const unsigned int
42898  w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
42899  h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
42900  if (w>max_width) max_width = w;
42901  if (h>max_height) max_height = h;
42902  sum_width+=w; sum_height+=h;
42903  if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
42904  else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
42905  }
42906  const CImg<uintT> indices0 = _indices>'x';
42907 
42908  // Create display window.
42909  if (!disp) {
42910  if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
42911  else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
42912  if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
42913  } else if (title) disp.set_title("%s",title);
42914  if (resize_disp) {
42915  if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
42916  else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
42917  }
42918 
42919  const unsigned int old_normalization = disp.normalization();
42920  bool old_is_resized = disp.is_resized();
42921  disp._normalization = 0;
42922  disp.show().set_key(0);
42923  const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
42924 
42925  // Enter event loop.
42926  CImg<ucharT> visu0, visu;
42927  CImg<uintT> indices;
42928  CImg<intT> positions(_width,4,1,1,-1);
42929  int oindice0 = -1, oindice1 = -1, indice0 = -1, indice1 = -1;
42930  bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
42931  unsigned int key = 0;
42932  while (!is_selected && !disp.is_closed() && !key) {
42933 
42934  // Create background image.
42935  if (!visu0) {
42936  visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
42937  (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
42938  unsigned int ind = 0;
42939  if (axis=='x') for (unsigned int x = 0; x<visu0._width; ) {
42940  const unsigned int x0 = x;
42941  ind = indices[x];
42942  while (x<indices._width && indices[++x]==ind) {}
42943  const CImg<T>
42944  &src = _data[ind],
42945  _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg<T>(),
42946  &img2d = _img2d?_img2d:src;
42947  CImg<ucharT> res = old_normalization==1?
42948  CImg<ucharT>(img2d.get_channels(0,cimg::min(2,img2d.spectrum()-1)).normalize(0,255)):
42949  CImg<ucharT>(img2d.get_channels(0,cimg::min(2,img2d.spectrum()-1)));
42950  const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
42951  res.resize(x - x0,cimg::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
42952  positions(ind,0) = positions(ind,2) = (int)x0;
42953  positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height()-res.height()));
42954  positions(ind,2)+=res._width;
42955  positions(ind,3)+=res._height - 1;
42956  visu0.draw_image(positions(ind,0),positions(ind,1),res);
42957  } else for (unsigned int y = 0; y<visu0._height; ) {
42958  const unsigned int y0 = y;
42959  ind = indices[y];
42960  while (y<visu0._height && indices[++y]==ind) {}
42961  const CImg<T>
42962  &src = _data[ind],
42963  _img2d = src._depth>1?src.get_projections2d(src._width/2,src._height/2,src._depth/2):CImg<T>(),
42964  &img2d = _img2d?_img2d:src;
42965  CImg<ucharT> res = old_normalization==1?CImg<ucharT>(img2d.get_normalize(0,255)):CImg<ucharT>(img2d);
42966  if (res._spectrum>3) res.channels(0,2);
42967  const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
42968  res.resize(cimg::max(32U,w*disp._width/max_width),y - y0,1,res._spectrum==1?3:-100);
42969  positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width()-res.width()));
42970  positions(ind,1) = positions(ind,3) = (int)y0;
42971  positions(ind,2)+=res._width - 1;
42972  positions(ind,3)+=res._height;
42973  visu0.draw_image(positions(ind,0),positions(ind,1),res);
42974  }
42975  if (axis=='x') --positions(ind,2); else --positions(ind,3);
42976  update_display = true;
42977  }
42978 
42979  if (!visu || oindice0!=indice0 || oindice1!=indice1) {
42980  if (indice0>=0 && indice1>=0) {
42981  visu.assign(visu0,false);
42982  const int indm = cimg::min(indice0,indice1), indM = cimg::max(indice0,indice1);
42983  for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
42984  visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),background_color,0.2f);
42985  if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
42986  (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
42987  visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),foreground_color,0.9f,0x55555555);
42988  }
42989  const int yt = (int)text_down?visu.height()-13:0;
42990  if (is_clicked) visu.draw_text(0,yt," Images %u - %u, Size = %u",foreground_color,background_color,0.7f,13,
42991  orig + indm,orig + indM,indM - indm + 1);
42992  else visu.draw_text(0,yt," Image %u",foreground_color,background_color,0.7f,13,
42993  orig + indice0);
42994  update_display = true;
42995  } else visu.assign();
42996  }
42997  if (!visu) { visu.assign(visu0,true); update_display = true; }
42998  if (update_display) { visu.display(disp); update_display = false; }
42999  disp.wait();
43000 
43001  // Manage user events.
43002  const int xm = disp.mouse_x(), ym = disp.mouse_y();
43003  int indice = -1;
43004 
43005  if (xm>=0) {
43006  indice = (int)indices(axis=='x'?xm:ym);
43007  if (disp.button()&1) {
43008  if (!is_clicked) { is_clicked = true; oindice0 = indice0; indice0 = indice; }
43009  oindice1 = indice1; indice1 = indice;
43010  if (!feature_type) is_selected = true;
43011  } else {
43012  if (!is_clicked) { oindice0 = oindice1 = indice0; indice0 = indice1 = indice; }
43013  else is_selected = true;
43014  }
43015  } else {
43016  if (is_clicked) {
43017  if (!(disp.button()&1)) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
43018  else indice1 = -1;
43019  } else indice0 = indice1 = -1;
43020  }
43021 
43022  if (disp.button()&4) { is_clicked = is_selected = false; indice0 = indice1 = -1; }
43023  if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; indice1 = indice0 = -1; }
43024  if (disp.wheel() && exit_on_wheel) is_selected = true;
43025 
43026  switch (key = disp.key()) {
43027 #if cimg_OS!=2
43028  case cimg::keyCTRLRIGHT :
43029 #endif
43030  case 0 : case cimg::keyCTRLLEFT : key = 0; break;
43031  case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43032  disp.set_fullscreen(false).resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
43033  CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
43034  _is_resized = true;
43035  disp.set_key(key,false); key = 0; visu0.assign();
43036  } break;
43037  case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43038  disp.set_fullscreen(false).resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
43039  disp.set_key(key,false); key = 0; visu0.assign();
43040  } break;
43041  case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43042  disp.set_fullscreen(false).resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false)._is_resized = true;
43043  disp.set_key(key,false); key = 0; visu0.assign();
43044  } break;
43045  case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43046  disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
43047  disp.set_key(key,false); key = 0; visu0.assign();
43048  } break;
43049  case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43050  static unsigned int snap_number = 0;
43051  char filename[32] = { 0 };
43052  std::FILE *file;
43053  do {
43054  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.bmp",snap_number++);
43055  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
43056  } while (file);
43057  if (visu0) {
43058  visu.draw_text(0,0," Saving snapshot... ",foreground_color,background_color,1,13).display(disp);
43059  visu0.save(filename);
43060  visu.draw_text(0,0," Snapshot '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
43061  }
43062  disp.set_key(key,false).wait(); key = 0;
43063  } break;
43064  case cimg::keyO :
43065  if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
43066  static unsigned int snap_number = 0;
43067  char filename[32] = { 0 };
43068  std::FILE *file;
43069  do {
43070 #ifdef cimg_use_zlib
43071  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimgz",snap_number++);
43072 #else
43073  cimg_snprintf(filename,sizeof(filename),cimg_appname "_%.4u.cimg",snap_number++);
43074 #endif
43075  if ((file=std::fopen(filename,"r"))!=0) cimg::fclose(file);
43076  } while (file);
43077  visu.draw_text(0,0," Saving instance... ",foreground_color,background_color,1,13).display(disp);
43078  save(filename);
43079  visu.draw_text(0,0," Instance '%s' saved. ",foreground_color,background_color,1,13,filename).display(disp);
43080  disp.set_key(key,false).wait(); key = 0;
43081  } break;
43082  }
43083  if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
43084  if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
43085  else if (ym>=visu.height()-13) { if(text_down) { visu.assign(); text_down = false; }}
43086  }
43087  CImg<intT> res(1,2,1,1,-1);
43088  if (is_selected) { if (feature_type) res.fill(cimg::min(indice0,indice1),cimg::max(indice0,indice1)); else res.fill(indice0); }
43089  if (!(disp.button()&2)) disp.set_button();
43090  disp._normalization = old_normalization;
43091  disp._is_resized = old_is_resized;
43092  disp.set_key(key);
43093  return res;
43094  }
43095 
43097 
43100  CImgList<T>& load(const char *const filename) {
43101  if (!filename)
43102  throw CImgArgumentException(_cimglist_instance
43103  "load(): Specified filename is (null).",
43104  cimglist_instance);
43105 
43106  if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
43107  char filename_local[1024] = { 0 };
43108  load(cimg::load_network_external(filename,filename_local));
43109  std::remove(filename_local);
43110  return *this;
43111  }
43112 
43113  const char *const ext = cimg::split_filename(filename);
43114  const unsigned int omode = cimg::exception_mode();
43115  cimg::exception_mode() = 0;
43116  try {
43117 #ifdef cimglist_load_plugin
43118  cimglist_load_plugin(filename);
43119 #endif
43120 #ifdef cimglist_load_plugin1
43121  cimglist_load_plugin1(filename);
43122 #endif
43123 #ifdef cimglist_load_plugin2
43124  cimglist_load_plugin2(filename);
43125 #endif
43126 #ifdef cimglist_load_plugin3
43127  cimglist_load_plugin3(filename);
43128 #endif
43129 #ifdef cimglist_load_plugin4
43130  cimglist_load_plugin4(filename);
43131 #endif
43132 #ifdef cimglist_load_plugin5
43133  cimglist_load_plugin5(filename);
43134 #endif
43135 #ifdef cimglist_load_plugin6
43136  cimglist_load_plugin6(filename);
43137 #endif
43138 #ifdef cimglist_load_plugin7
43139  cimglist_load_plugin7(filename);
43140 #endif
43141 #ifdef cimglist_load_plugin8
43142  cimglist_load_plugin8(filename);
43143 #endif
43144  if (!cimg::strcasecmp(ext,"tif") ||
43145  !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
43146  else if (!cimg::strcasecmp(ext,"cimg") ||
43147  !cimg::strcasecmp(ext,"cimgz") ||
43148  !*ext) load_cimg(filename);
43149  else if (!cimg::strcasecmp(ext,"rec") ||
43150  !cimg::strcasecmp(ext,"par")) load_parrec(filename);
43151  else if (!cimg::strcasecmp(ext,"avi") ||
43152  !cimg::strcasecmp(ext,"mov") ||
43153  !cimg::strcasecmp(ext,"asf") ||
43154  !cimg::strcasecmp(ext,"divx") ||
43155  !cimg::strcasecmp(ext,"flv") ||
43156  !cimg::strcasecmp(ext,"mpg") ||
43157  !cimg::strcasecmp(ext,"m1v") ||
43158  !cimg::strcasecmp(ext,"m2v") ||
43159  !cimg::strcasecmp(ext,"m4v") ||
43160  !cimg::strcasecmp(ext,"mjp") ||
43161  !cimg::strcasecmp(ext,"mkv") ||
43162  !cimg::strcasecmp(ext,"mpe") ||
43163  !cimg::strcasecmp(ext,"movie") ||
43164  !cimg::strcasecmp(ext,"ogm") ||
43165  !cimg::strcasecmp(ext,"ogg") ||
43166  !cimg::strcasecmp(ext,"qt") ||
43167  !cimg::strcasecmp(ext,"rm") ||
43168  !cimg::strcasecmp(ext,"vob") ||
43169  !cimg::strcasecmp(ext,"wmv") ||
43170  !cimg::strcasecmp(ext,"xvid") ||
43171  !cimg::strcasecmp(ext,"mpeg")) load_ffmpeg(filename);
43172  else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
43173  else throw CImgIOException("CImgList<%s>::load()",
43174  pixel_type());
43175  } catch (CImgIOException&) {
43176  try {
43177  cimg::fclose(cimg::fopen(filename,"rb"));
43178  } catch (CImgIOException&) {
43179  cimg::exception_mode() = omode;
43180  throw CImgIOException(_cimglist_instance
43181  "load(): Failed to open file '%s'.",
43182  cimglist_instance,
43183  filename);
43184  }
43185  assign(1);
43186  try {
43187  _data->load(filename);
43188  } catch (CImgIOException&) {
43189  cimg::exception_mode() = omode;
43190  throw CImgIOException(_cimglist_instance
43191  "load(): Failed to recognize format of file '%s'.",
43192  cimglist_instance,
43193  filename);
43194  }
43195  }
43196  cimg::exception_mode() = omode;
43197  return *this;
43198  }
43199 
43201  static CImgList<T> get_load(const char *const filename) {
43202  return CImgList<T>().load(filename);
43203  }
43204 
43206 
43209  CImgList<T>& load_cimg(const char *const filename) {
43210  return _load_cimg(0,filename);
43211  }
43212 
43214  static CImgList<T> get_load_cimg(const char *const filename) {
43215  return CImgList<T>().load_cimg(filename);
43216  }
43217 
43219 
43222  CImgList<T>& load_cimg(std::FILE *const file) {
43223  return _load_cimg(file,0);
43224  }
43225 
43227  static CImgList<T> get_load_cimg(std::FILE *const file) {
43228  return CImgList<T>().load_cimg(file);
43229  }
43230 
43231  CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
43232 #ifdef cimg_use_zlib
43233 #define _cimgz_load_cimg_case(Tss) { \
43234  Bytef *const cbuf = new Bytef[csiz]; \
43235  cimg::fread(cbuf,csiz,nfile); \
43236  raw.assign(W,H,D,C); \
43237  unsigned long destlen = (unsigned long)raw.size()*sizeof(Tss); \
43238  uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
43239  delete[] cbuf; \
43240  const Tss *ptrs = raw._data; \
43241  for (unsigned long off = raw.size(); off; --off) *(ptrd++) = (T)*(ptrs++); \
43242 }
43243 #else
43244 #define _cimgz_load_cimg_case(Tss) \
43245  throw CImgIOException(_cimglist_instance \
43246  "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
43247  cimglist_instance, \
43248  filename?filename:"(FILE*)");
43249 #endif
43250 
43251 #define _cimg_load_cimg_case(Ts,Tss) \
43252  if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
43253  for (unsigned int l = 0; l<N; ++l) { \
43254  j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
43255  W = H = D = C = 0; csiz = 0; \
43256  if ((err = std::sscanf(tmp,"%u %u %u %u #%u",&W,&H,&D,&C,&csiz))<4) \
43257  throw CImgIOException(_cimglist_instance \
43258  "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
43259  cimglist_instance, \
43260  W,H,D,C,l,filename?filename:("(FILE*)")); \
43261  if (W*H*D*C>0) { \
43262  CImg<Tss> raw; \
43263  CImg<T> &img = _data[l]; \
43264  img.assign(W,H,D,C); \
43265  T *ptrd = img._data; \
43266  if (err==5) _cimgz_load_cimg_case(Tss) \
43267  else for (long to_read = (long)img.size(); to_read>0; ) { \
43268  raw.assign(cimg::min(to_read,cimg_iobuffer)); \
43269  cimg::fread(raw._data,raw._width,nfile); \
43270  if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
43271  to_read-=raw._width; \
43272  const Tss *ptrs = raw._data; \
43273  for (unsigned long off = (unsigned long)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
43274  } \
43275  } \
43276  } \
43277  loaded = true; \
43278  }
43279 
43280  if (!filename && !file)
43281  throw CImgArgumentException(_cimglist_instance
43282  "load_cimg(): Specified filename is (null).",
43283  cimglist_instance);
43284 
43285  const int cimg_iobuffer = 12*1024*1024;
43286  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
43287  bool loaded = false, endian = cimg::endianness();
43288  char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
43289  unsigned int j, err, N = 0, W, H, D, C, csiz;
43290  int i;
43291  do {
43292  j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
43293  } while (*tmp=='#' && i!=EOF);
43294  err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
43295  if (err<2) {
43296  if (!file) cimg::fclose(nfile);
43297  throw CImgIOException(_cimglist_instance
43298  "load_cimg(): CImg header not found in file '%s'.",
43299  cimglist_instance,
43300  filename?filename:"(FILE*)");
43301  }
43302  if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
43303  else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
43304  assign(N);
43305  _cimg_load_cimg_case("bool",bool);
43306  _cimg_load_cimg_case("unsigned_char",unsigned char);
43307  _cimg_load_cimg_case("uchar",unsigned char);
43308  _cimg_load_cimg_case("char",char);
43309  _cimg_load_cimg_case("unsigned_short",unsigned short);
43310  _cimg_load_cimg_case("ushort",unsigned short);
43311  _cimg_load_cimg_case("short",short);
43312  _cimg_load_cimg_case("unsigned_int",unsigned int);
43313  _cimg_load_cimg_case("uint",unsigned int);
43314  _cimg_load_cimg_case("int",int);
43315  _cimg_load_cimg_case("unsigned_long",unsigned long);
43316  _cimg_load_cimg_case("ulong",unsigned long);
43317  _cimg_load_cimg_case("long",long);
43318  _cimg_load_cimg_case("float",float);
43319  _cimg_load_cimg_case("double",double);
43320  if (!loaded) {
43321  if (!file) cimg::fclose(nfile);
43322  throw CImgIOException(_cimglist_instance
43323  "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
43324  cimglist_instance,
43325  str_pixeltype,filename?filename:"(FILE*)");
43326  }
43327  if (!file) cimg::fclose(nfile);
43328  return *this;
43329  }
43330 
43332 
43345  CImgList<T>& load_cimg(const char *const filename,
43346  const unsigned int n0, const unsigned int n1,
43347  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43348  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43349  return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
43350  }
43351 
43353  static CImgList<T> get_load_cimg(const char *const filename,
43354  const unsigned int n0, const unsigned int n1,
43355  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43356  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43357  return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
43358  }
43359 
43361 
43374  CImgList<T>& load_cimg(std::FILE *const file,
43375  const unsigned int n0, const unsigned int n1,
43376  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43377  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43378  return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
43379  }
43380 
43382  static CImgList<T> get_load_cimg(std::FILE *const file,
43383  const unsigned int n0, const unsigned int n1,
43384  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43385  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43386  return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
43387  }
43388 
43389  CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
43390  const unsigned int n0, const unsigned int n1,
43391  const unsigned int x0, const unsigned int y0, const unsigned int z0, const unsigned int c0,
43392  const unsigned int x1, const unsigned int y1, const unsigned int z1, const unsigned int c1) {
43393 #define _cimg_load_cimg_case2(Ts,Tss) \
43394  if (!loaded && !cimg::strcasecmp(Ts,str_pixeltype)) { \
43395  for (unsigned int l = 0; l<=nn1; ++l) { \
43396  j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
43397  W = H = D = C = 0; \
43398  if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
43399  throw CImgIOException(_cimglist_instance \
43400  "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
43401  cimglist_instance, \
43402  W,H,D,C,l,filename?filename:"(FILE*)"); \
43403  if (W*H*D*C>0) { \
43404  if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
43405  else { \
43406  const unsigned int \
43407  nx1 = x1>=W?W-1:x1, \
43408  ny1 = y1>=H?H-1:y1, \
43409  nz1 = z1>=D?D-1:z1, \
43410  nc1 = c1>=C?C-1:c1; \
43411  CImg<Tss> raw(1 + nx1 - x0); \
43412  CImg<T> &img = _data[l - n0]; \
43413  img.assign(1 + nx1 - x0,1 + ny1 - y0,1 + nz1 - z0,1 + nc1 - c0); \
43414  T *ptrd = img._data; \
43415  const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
43416  if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
43417  for (unsigned int v = 1 + nc1 - c0; v; --v) { \
43418  const unsigned int skipzb = z0*W*H*sizeof(Tss); \
43419  if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
43420  for (unsigned int z = 1 + nz1 - z0; z; --z) { \
43421  const unsigned int skipyb = y0*W*sizeof(Tss); \
43422  if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
43423  for (unsigned int y = 1 + ny1 - y0; y; --y) { \
43424  const unsigned int skipxb = x0*sizeof(Tss); \
43425  if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
43426  cimg::fread(raw._data,raw._width,nfile); \
43427  if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
43428  const Tss *ptrs = raw._data; \
43429  for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
43430  const unsigned int skipxe = (W-1-nx1)*sizeof(Tss); \
43431  if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
43432  } \
43433  const unsigned int skipye = (H-1-ny1)*W*sizeof(Tss); \
43434  if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
43435  } \
43436  const unsigned int skipze = (D-1-nz1)*W*H*sizeof(Tss); \
43437  if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
43438  } \
43439  const unsigned int skipve = (C-1-nc1)*W*H*D*sizeof(Tss); \
43440  if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
43441  } \
43442  } \
43443  } \
43444  loaded = true; \
43445  }
43446 
43447  if (!filename && !file)
43448  throw CImgArgumentException(_cimglist_instance
43449  "load_cimg(): Specified filename is (null).",
43450  cimglist_instance);
43451 
43452  if (n1<n0 || x1<x0 || y1<y0 || z1<z0 || c1<c0)
43453  throw CImgArgumentException(_cimglist_instance
43454  "load_cimg(): Invalid specified sub-region coordinates [%u->%u] (%u,%u,%u,%u)->(%u,%u,%u,%u) for file '%s'.",
43455  cimglist_instance,
43456  n0,n1,x0,y0,z0,c0,x1,y1,z1,filename?filename:"(FILE*)");
43457 
43458  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
43459  bool loaded = false, endian = cimg::endianness();
43460  char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
43461  unsigned int j, err, N, W, H, D, C;
43462  int i;
43463  j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
43464  err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
43465  if (err<2) {
43466  if (!file) cimg::fclose(nfile);
43467  throw CImgIOException(_cimglist_instance
43468  "load_cimg(): CImg header not found in file '%s'.",
43469  cimglist_instance,
43470  filename?filename:"(FILE*)");
43471  }
43472  if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
43473  else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
43474  const unsigned int nn1 = n1>=N?N-1:n1;
43475  assign(1+nn1-n0);
43476  _cimg_load_cimg_case2("bool",bool);
43477  _cimg_load_cimg_case2("unsigned_char",unsigned char);
43478  _cimg_load_cimg_case2("uchar",unsigned char);
43479  _cimg_load_cimg_case2("char",char);
43480  _cimg_load_cimg_case2("unsigned_short",unsigned short);
43481  _cimg_load_cimg_case2("ushort",unsigned short);
43482  _cimg_load_cimg_case2("short",short);
43483  _cimg_load_cimg_case2("unsigned_int",unsigned int);
43484  _cimg_load_cimg_case2("uint",unsigned int);
43485  _cimg_load_cimg_case2("int",int);
43486  _cimg_load_cimg_case2("unsigned_long",unsigned long);
43487  _cimg_load_cimg_case2("ulong",unsigned long);
43488  _cimg_load_cimg_case2("long",long);
43489  _cimg_load_cimg_case2("float",float);
43490  _cimg_load_cimg_case2("double",double);
43491  if (!loaded) {
43492  if (!file) cimg::fclose(nfile);
43493  throw CImgIOException(_cimglist_instance
43494  "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
43495  cimglist_instance,
43496  str_pixeltype,filename?filename:"(FILE*)");
43497  }
43498  if (!file) cimg::fclose(nfile);
43499  return *this;
43500  }
43501 
43503 
43506  CImgList<T>& load_parrec(const char *const filename) {
43507  if (!filename)
43508  throw CImgArgumentException(_cimglist_instance
43509  "load_parrec(): Specified filename is (null).",
43510  cimglist_instance);
43511 
43512  char body[1024] = { 0 }, filenamepar[1024] = { 0 }, filenamerec[1024] = { 0 };
43513  const char *const ext = cimg::split_filename(filename,body);
43514  if (!std::strcmp(ext,"par")) { std::strncpy(filenamepar,filename,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.rec",body); }
43515  if (!std::strcmp(ext,"PAR")) { std::strncpy(filenamepar,filename,sizeof(filenamepar)-1); cimg_snprintf(filenamerec,sizeof(filenamerec),"%s.REC",body); }
43516  if (!std::strcmp(ext,"rec")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.par",body); }
43517  if (!std::strcmp(ext,"REC")) { std::strncpy(filenamerec,filename,sizeof(filenamerec)-1); cimg_snprintf(filenamepar,sizeof(filenamepar),"%s.PAR",body); }
43518  std::FILE *file = cimg::fopen(filenamepar,"r");
43519 
43520  // Parse header file
43521  CImgList<floatT> st_slices;
43522  CImgList<uintT> st_global;
43523  int err;
43524  char line[256] = { 0 };
43525  do { err=std::fscanf(file,"%255[^\n]%*c",line); } while (err!=EOF && (*line=='#' || *line=='.'));
43526  do {
43527  unsigned int sn,size_x,size_y,pixsize;
43528  float rs,ri,ss;
43529  err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss);
43530  if (err==7) {
43531  CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
43532  unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
43533  if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
43534  else {
43535  CImg<uintT> &vec = st_global[i];
43536  if (size_x>vec[0]) vec[0] = size_x;
43537  if (size_y>vec[1]) vec[1] = size_y;
43538  vec[2] = sn;
43539  }
43540  st_slices[st_slices._width-1][7] = (float)i;
43541  }
43542  } while (err==7);
43543 
43544  // Read data
43545  std::FILE *file2 = cimg::fopen(filenamerec,"rb");
43546  cimglist_for(st_global,l) {
43547  const CImg<uintT>& vec = st_global[l];
43548  CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
43549  }
43550 
43551  cimglist_for(st_slices,l) {
43552  const CImg<floatT>& vec = st_slices[l];
43553  const unsigned int
43554  sn = (unsigned int)vec[0] - 1,
43555  pixsize = (unsigned int)vec[1],
43556  size_x = (unsigned int)vec[2],
43557  size_y = (unsigned int)vec[3],
43558  imn = (unsigned int)vec[7];
43559  const float ri = vec[4], rs = vec[5], ss = vec[6];
43560  switch (pixsize) {
43561  case 8 : {
43562  CImg<ucharT> buf(size_x,size_y);
43563  cimg::fread(buf._data,size_x*size_y,file2);
43564  if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
43565  CImg<T>& img = (*this)[imn];
43566  cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
43567  } break;
43568  case 16 : {
43569  CImg<ushortT> buf(size_x,size_y);
43570  cimg::fread(buf._data,size_x*size_y,file2);
43571  if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
43572  CImg<T>& img = (*this)[imn];
43573  cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
43574  } break;
43575  case 32 : {
43576  CImg<uintT> buf(size_x,size_y);
43577  cimg::fread(buf._data,size_x*size_y,file2);
43578  if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
43579  CImg<T>& img = (*this)[imn];
43580  cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
43581  } break;
43582  default :
43583  cimg::fclose(file);
43584  cimg::fclose(file2);
43585  throw CImgIOException(_cimglist_instance
43586  "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
43587  cimglist_instance,
43588  pixsize,filename);
43589  }
43590  }
43591  cimg::fclose(file);
43592  cimg::fclose(file2);
43593  if (!_width)
43594  throw CImgIOException(_cimglist_instance
43595  "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
43596  cimglist_instance,
43597  filename);
43598  return *this;
43599  }
43600 
43602  static CImgList<T> get_load_parrec(const char *const filename) {
43603  return CImgList<T>().load_parrec(filename);
43604  }
43605 
43607 
43616  CImgList<T>& load_yuv(const char *const filename,
43617  const unsigned int size_x, const unsigned int size_y,
43618  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43619  const unsigned int step_frame=1, const bool yuv2rgb=true) {
43620  return _load_yuv(0,filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
43621  }
43622 
43624  static CImgList<T> get_load_yuv(const char *const filename,
43625  const unsigned int size_x, const unsigned int size_y=1,
43626  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43627  const unsigned int step_frame=1, const bool yuv2rgb=true) {
43628  return CImgList<T>().load_yuv(filename,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
43629  }
43630 
43632  CImgList<T>& load_yuv(std::FILE *const file,
43633  const unsigned int size_x, const unsigned int size_y,
43634  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43635  const unsigned int step_frame=1, const bool yuv2rgb=true) {
43636  return _load_yuv(file,0,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
43637  }
43638 
43640  static CImgList<T> get_load_yuv(std::FILE *const file,
43641  const unsigned int size_x, const unsigned int size_y=1,
43642  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43643  const unsigned int step_frame=1, const bool yuv2rgb=true) {
43644  return CImgList<T>().load_yuv(file,size_x,size_y,first_frame,last_frame,step_frame,yuv2rgb);
43645  }
43646 
43647  CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
43648  const unsigned int size_x, const unsigned int size_y,
43649  const unsigned int first_frame, const unsigned int last_frame,
43650  const unsigned int step_frame, const bool yuv2rgb) {
43651  if (!filename && !file)
43652  throw CImgArgumentException(_cimglist_instance
43653  "load_yuv(): Specified filename is (null).",
43654  cimglist_instance);
43655  if (size_x%2 || size_y%2)
43656  throw CImgArgumentException(_cimglist_instance
43657  "load_yuv(): Invalid odd XY dimensions %ux%u in file '%s'.",
43658  cimglist_instance,
43659  size_x,size_y,filename?filename:"(FILE*)");
43660  if (!size_x || !size_y)
43661  throw CImgArgumentException(_cimglist_instance
43662  "load_yuv(): Invalid sequence size (%u,%u) in file '%s'.",
43663  cimglist_instance,
43664  size_x,size_y,filename?filename:"(FILE*)");
43665 
43666  const unsigned int
43667  nfirst_frame = first_frame<last_frame?first_frame:last_frame,
43668  nlast_frame = first_frame<last_frame?last_frame:first_frame,
43669  nstep_frame = step_frame?step_frame:1;
43670 
43671  CImg<ucharT> tmp(size_x,size_y,1,3), UV(size_x/2,size_y/2,1,2);
43672  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
43673  bool stopflag = false;
43674  int err;
43675  if (nfirst_frame) {
43676  err = std::fseek(nfile,nfirst_frame*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
43677  if (err) {
43678  if (!file) cimg::fclose(nfile);
43679  throw CImgIOException(_cimglist_instance
43680  "load_yuv(): File '%s' doesn't contain frame number %u.",
43681  cimglist_instance,
43682  filename?filename:"(FILE*)",nfirst_frame);
43683  }
43684  }
43685  unsigned int frame;
43686  for (frame = nfirst_frame; !stopflag && frame<=nlast_frame; frame+=nstep_frame) {
43687  tmp.fill(0);
43688  // *TRY* to read the luminance part, do not replace by cimg::fread!
43689  err = (int)std::fread((void*)(tmp._data),1,(unsigned long)tmp._width*tmp._height,nfile);
43690  if (err!=(int)(tmp._width*tmp._height)) {
43691  stopflag = true;
43692  if (err>0)
43693  cimg::warn(_cimglist_instance
43694  "load_yuv(): File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.",
43695  cimglist_instance,
43696  filename?filename:"(FILE*)",size_x,size_y);
43697  } else {
43698  UV.fill(0);
43699  // *TRY* to read the luminance part, do not replace by cimg::fread!
43700  err = (int)std::fread((void*)(UV._data),1,(size_t)(UV.size()),nfile);
43701  if (err!=(int)(UV.size())) {
43702  stopflag = true;
43703  if (err>0)
43704  cimg::warn(_cimglist_instance
43705  "load_yuv(): File '%s' contains incomplete data or given image dimensions (%u,%u) are incorrect.",
43706  cimglist_instance,
43707  filename?filename:"(FILE*)",size_x,size_y);
43708  } else {
43709  cimg_forXY(UV,x,y) {
43710  const int x2 = x*2, y2 = y*2;
43711  tmp(x2,y2,1) = tmp(x2+1,y2,1) = tmp(x2,y2+1,1) = tmp(x2+1,y2+1,1) = UV(x,y,0);
43712  tmp(x2,y2,2) = tmp(x2+1,y2,2) = tmp(x2,y2+1,2) = tmp(x2+1,y2+1,2) = UV(x,y,1);
43713  }
43714  if (yuv2rgb) tmp.YCbCrtoRGB();
43715  insert(tmp);
43716  if (nstep_frame>1) std::fseek(nfile,(nstep_frame-1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
43717  }
43718  }
43719  }
43720  if (stopflag && nlast_frame!=~0U && frame!=nlast_frame)
43721  cimg::warn(_cimglist_instance
43722  "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
43723  cimglist_instance,
43724  nlast_frame,frame-1,filename?filename:"(FILE*)");
43725 
43726  if (!file) cimg::fclose(nfile);
43727  return *this;
43728  }
43729 
43731 
43739  // This piece of code has been firstly created by David Starweather (starkdg(at)users(dot)sourceforge(dot)net)
43740  // I modified it afterwards for direct inclusion in the library core.
43741  CImgList<T>& load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43742  const unsigned int step_frame=1, const bool pixel_format=true, const bool resume=false) {
43743  if (!filename)
43744  throw CImgArgumentException(_cimglist_instance
43745  "load_ffmpeg(): Specified filename is (null).",
43746  cimglist_instance);
43747 
43748  const unsigned int
43749  nfirst_frame = first_frame<last_frame?first_frame:last_frame,
43750  nlast_frame = first_frame<last_frame?last_frame:first_frame,
43751  nstep_frame = step_frame?step_frame:1;
43752  assign();
43753 
43754 #ifndef cimg_use_ffmpeg
43755  if ((nfirst_frame || nlast_frame!=~0U || nstep_frame>1) || (resume && (pixel_format || !pixel_format)))
43756  throw CImgArgumentException(_cimglist_instance
43757  "load_ffmpeg(): Unable to load sub-frames from file '%s' unless libffmpeg is enabled.",
43758  cimglist_instance,
43759  filename);
43760 
43761  return load_ffmpeg_external(filename);
43762 #else
43763  const PixelFormat ffmpeg_pixfmt = pixel_format?PIX_FMT_RGB24:PIX_FMT_GRAY8;
43764  avcodec_register_all();
43765  av_register_all();
43766  static AVFormatContext *format_ctx = 0;
43767  static AVCodecContext *codec_ctx = 0;
43768  static AVCodec *codec = 0;
43769  static AVFrame *avframe = avcodec_alloc_frame(), *converted_frame = avcodec_alloc_frame();
43770  static int vstream = 0;
43771 
43772  if (resume) {
43773  if (!format_ctx || !codec_ctx || !codec || !avframe || !converted_frame)
43774  throw CImgArgumentException(_cimglist_instance
43775  "load_ffmpeg(): Failed to resume loading of file '%s', due to unallocated FFMPEG structures.",
43776  cimglist_instance,
43777  filename);
43778  } else {
43779  // Open video file, find main video stream and codec.
43780  if (format_ctx) av_close_input_file(format_ctx);
43781  if (av_open_input_file(&format_ctx,filename,0,0,0)!=0)
43782  throw CImgIOException(_cimglist_instance
43783  "load_ffmpeg(): Failed to open file '%s'.",
43784  cimglist_instance,
43785  filename);
43786 
43787  if (!avframe || !converted_frame || av_find_stream_info(format_ctx)<0) {
43788  av_close_input_file(format_ctx); format_ctx = 0;
43789  return load_ffmpeg_external(filename);
43790  }
43791 #if cimg_verbosity>=3
43792  dump_format(format_ctx,0,0,0);
43793 #endif
43794 
43795  // Special command: Return informations on main video stream.
43796  // as a vector 1x4 containing: (nb_frames,width,height,fps).
43797  if (!first_frame && !last_frame && !step_frame) {
43798  for (vstream = 0; vstream<(int)(format_ctx->nb_streams); ++vstream)
43799  if (format_ctx->streams[vstream]->codec->codec_type==CODEC_TYPE_VIDEO) break;
43800  if (vstream==(int)format_ctx->nb_streams) assign();
43801  else {
43802  CImgList<doubleT> timestamps;
43803  int nb_frames;
43804  AVPacket packet;
43805  // Count frames and store timestamps.
43806  for (nb_frames = 0; av_read_frame(format_ctx,&packet)>=0; av_free_packet(&packet))
43807  if (packet.stream_index==vstream) {
43808  CImg<doubleT>::vector((double)packet.pts).move_to(timestamps);
43809  ++nb_frames;
43810  }
43811  // Get frame with, height and fps.
43812  const int
43813  framew = format_ctx->streams[vstream]->codec->width,
43814  frameh = format_ctx->streams[vstream]->codec->height;
43815  const float
43816  num = (float)(format_ctx->streams[vstream]->r_frame_rate).num,
43817  den = (float)(format_ctx->streams[vstream]->r_frame_rate).den,
43818  fps = num/den;
43819  // Return infos as a list.
43820  assign(2);
43821  (*this)[0].assign(1,4).fill((T)nb_frames,(T)framew,(T)frameh,(T)fps);
43822  (*this)[1] = (timestamps>'y');
43823  }
43824  av_close_input_file(format_ctx); format_ctx = 0;
43825  return *this;
43826  }
43827 
43828  for (vstream = 0; vstream<(int)(format_ctx->nb_streams) &&
43829  format_ctx->streams[vstream]->codec->codec_type!=CODEC_TYPE_VIDEO; ) ++vstream;
43830  if (vstream==(int)format_ctx->nb_streams) {
43831  av_close_input_file(format_ctx); format_ctx = 0;
43832  return load_ffmpeg_external(filename);
43833  }
43834  codec_ctx = format_ctx->streams[vstream]->codec;
43835  codec = avcodec_find_decoder(codec_ctx->codec_id);
43836  if (!codec) {
43837  return load_ffmpeg_external(filename);
43838  }
43839  if (avcodec_open(codec_ctx,codec)<0) { // Open codec
43840  return load_ffmpeg_external(filename);
43841  }
43842  }
43843 
43844  // Read video frames
43845  const unsigned int numBytes = avpicture_get_size(ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
43846  uint8_t *const buffer = new uint8_t[numBytes];
43847  avpicture_fill((AVPicture *)converted_frame,buffer,ffmpeg_pixfmt,codec_ctx->width,codec_ctx->height);
43848  const T foo = (T)0;
43849  AVPacket packet;
43850  for (unsigned int frame = 0, next_frame = nfirst_frame; frame<=nlast_frame && av_read_frame(format_ctx,&packet)>=0; ) {
43851  if (packet.stream_index==(int)vstream) {
43852  int decoded = 0;
43853 #if defined(AV_VERSION_INT)
43854 #if LIBAVCODEC_VERSION_INT<AV_VERSION_INT(52,26,0)
43855  avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data, packet.size);
43856 #else
43857  avcodec_decode_video2(codec_ctx,avframe,&decoded,&packet);
43858 #endif
43859 #else
43860  avcodec_decode_video(codec_ctx,avframe,&decoded,packet.data, packet.size);
43861 #endif
43862  if (decoded) {
43863  if (frame==next_frame) {
43864  SwsContext *c = sws_getContext(codec_ctx->width,codec_ctx->height,codec_ctx->pix_fmt,codec_ctx->width,
43865  codec_ctx->height,ffmpeg_pixfmt,1,0,0,0);
43866  sws_scale(c,avframe->data,avframe->linesize,0,codec_ctx->height,converted_frame->data,converted_frame->linesize);
43867  if (ffmpeg_pixfmt==PIX_FMT_RGB24) {
43868  CImg<ucharT> next_image(*converted_frame->data,3,codec_ctx->width,codec_ctx->height,1,true);
43869  next_image._get_permute_axes("yzcx",foo).move_to(*this);
43870  } else {
43871  CImg<ucharT> next_image(*converted_frame->data,1,codec_ctx->width,codec_ctx->height,1,true);
43872  next_image._get_permute_axes("yzcx",foo).move_to(*this);
43873  }
43874  next_frame+=nstep_frame;
43875  }
43876  ++frame;
43877  }
43878  av_free_packet(&packet);
43879  if (next_frame>nlast_frame) break;
43880  }
43881  }
43882  delete[] buffer;
43883 #endif
43884  return *this;
43885  }
43886 
43888  static CImgList<T> get_load_ffmpeg(const char *const filename, const unsigned int first_frame=0, const unsigned int last_frame=~0U,
43889  const unsigned int step_frame=1, const bool pixel_format=true) {
43890  return CImgList<T>().load_ffmpeg(filename,first_frame,last_frame,step_frame,pixel_format);
43891  }
43892 
43894 
43897  CImgList<T>& load_ffmpeg_external(const char *const filename) {
43898  if (!filename)
43899  throw CImgArgumentException(_cimglist_instance
43900  "load_ffmpeg_external(): Specified filename is (null).",
43901  cimglist_instance);
43902  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
43903  char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
43904  std::FILE *file = 0;
43905  do {
43906  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
43907  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp);
43908  if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
43909  } while (file);
43910  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%%6d.ppm",filetmp);
43911 #if cimg_OS!=2
43912  cimg_snprintf(command,sizeof(command),"%s -i \"%s\" %s >/dev/null 2>&1",cimg::ffmpeg_path(),filename,filetmp2);
43913 #else
43914  cimg_snprintf(command,sizeof(command),"\"%s -i \"%s\" %s\" >NUL 2>&1",cimg::ffmpeg_path(),filename,filetmp2);
43915 #endif
43916  cimg::system(command,0);
43917  const unsigned int omode = cimg::exception_mode();
43918  cimg::exception_mode() = 0;
43919  assign();
43920  unsigned int i = 1;
43921  for (bool stopflag = false; !stopflag; ++i) {
43922  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,i);
43923  CImg<T> img;
43924  try { img.load_pnm(filetmp2); }
43925  catch (CImgException&) { stopflag = true; }
43926  if (img) { img.move_to(*this); std::remove(filetmp2); }
43927  }
43928  cimg::exception_mode() = omode;
43929  if (is_empty())
43930  throw CImgIOException(_cimglist_instance
43931  "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
43932  cimglist_instance,
43933  filename);
43934  return *this;
43935  }
43936 
43938  static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
43939  return CImgList<T>().load_ffmpeg_external(filename);
43940  }
43941 
43943 
43946  CImgList<T>& load_gzip_external(const char *const filename) {
43947  if (!filename)
43948  throw CImgIOException(_cimglist_instance
43949  "load_gzip_external(): Specified filename is (null).",
43950  cimglist_instance);
43951  std::fclose(cimg::fopen(filename,"rb")); // Check if file exists.
43952  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
43953  const char
43954  *ext = cimg::split_filename(filename,body),
43955  *ext2 = cimg::split_filename(body,0);
43956  std::FILE *file = 0;
43957  do {
43958  if (!cimg::strcasecmp(ext,"gz")) {
43959  if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
43960  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
43961  } else {
43962  if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
43963  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
43964  }
43965  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
43966  } while (file);
43967  cimg_snprintf(command,sizeof(command),"%s -c \"%s\" > %s",cimg::gunzip_path(),filename,filetmp);
43968  cimg::system(command);
43969  if (!(file = std::fopen(filetmp,"rb"))) {
43970  cimg::fclose(cimg::fopen(filename,"r"));
43971  throw CImgIOException(_cimglist_instance
43972  "load_gzip_external(): Failed to open file '%s'.",
43973  cimglist_instance,
43974  filename);
43975 
43976  } else cimg::fclose(file);
43977  load(filetmp);
43978  std::remove(filetmp);
43979  return *this;
43980  }
43981 
43983  static CImgList<T> get_load_gzip_external(const char *const filename) {
43984  return CImgList<T>().load_gzip_external(filename);
43985  }
43986 
43988 
43994  template<typename tf, typename tc>
43995  CImgList<T>& load_off(const char *const filename,
43996  CImgList<tf>& primitives, CImgList<tc>& colors) {
43997  return get_load_off(filename,primitives,colors).move_to(*this);
43998  }
43999 
44001  template<typename tf, typename tc>
44002  static CImgList<T> get_load_off(const char *const filename,
44003  CImgList<tf>& primitives, CImgList<tc>& colors) {
44004  return CImg<T>().load_off(filename,primitives,colors)<'x';
44005  }
44006 
44008 
44014  CImgList<T>& load_tiff(const char *const filename,
44015  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
44016  const unsigned int step_frame=1) {
44017  const unsigned int
44018  nfirst_frame = first_frame<last_frame?first_frame:last_frame,
44019  nstep_frame = step_frame?step_frame:1;
44020  unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
44021 #ifndef cimg_use_tiff
44022  if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
44023  throw CImgArgumentException(_cimglist_instance
44024  "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
44025  cimglist_instance,
44026  filename);
44027 
44028  return assign(CImg<T>::get_load_tiff(filename));
44029 #else
44030  TIFF *tif = TIFFOpen(filename,"r");
44031  if (tif) {
44032  unsigned int nb_images = 0;
44033  do ++nb_images; while (TIFFReadDirectory(tif));
44034  if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
44035  cimg::warn(_cimglist_instance
44036  "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since file '%s' contains %u image(s).",
44037  cimglist_instance,
44038  nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
44039 
44040  if (nfirst_frame>=nb_images) return assign();
44041  if (nlast_frame>=nb_images) nlast_frame = nb_images-1;
44042  assign(1+(nlast_frame-nfirst_frame)/nstep_frame);
44043  TIFFSetDirectory(tif,0);
44044 #if cimg_verbosity>=3
44045  TIFFSetWarningHandler(0);
44046  TIFFSetErrorHandler(0);
44047 #endif
44048  cimglist_for(*this,l) _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame);
44049  TIFFClose(tif);
44050  } else throw CImgIOException(_cimglist_instance
44051  "load_tiff(): Failed to open file '%s'.",
44052  cimglist_instance,
44053  filename);
44054  return *this;
44055 #endif
44056  }
44057 
44059  static CImgList<T> get_load_tiff(const char *const filename,
44060  const unsigned int first_frame=0, const unsigned int last_frame=~0U,
44061  const unsigned int step_frame=1) {
44062  return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame);
44063  }
44064 
44066  //----------------------------------
44067  //
44069 
44070  //----------------------------------
44071 
44073 
44077  const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
44078  unsigned int msiz = 0;
44079  cimglist_for(*this,l) msiz+=_data[l].size();
44080  msiz*=sizeof(T);
44081  const unsigned int mdisp = msiz<8*1024?0:(msiz<8*1024*1024?1:2);
44082  char _title[64] = { 0 };
44083  if (!title) cimg_snprintf(_title,sizeof(_title),"CImgList<%s>",pixel_type());
44084  std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
44085  cimg::t_magenta,cimg::t_bold,title?title:_title,cimg::t_normal,
44086  cimg::t_bold,cimg::t_normal,(void*)this,
44087  cimg::t_bold,cimg::t_normal,_width,_allocated_width,
44088  mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
44089  mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
44090  cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
44091  if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end()-1));
44092  else std::fprintf(cimg::output(),".\n");
44093 
44094  char tmp[16] = { 0 };
44095  cimglist_for(*this,ll) {
44096  cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
44097  std::fprintf(cimg::output()," ");
44098  _data[ll].print(tmp,display_stats);
44099  if (ll==3 && _width>8) { ll = _width-5; std::fprintf(cimg::output()," ...\n"); }
44100  }
44101  std::fflush(cimg::output());
44102  return *this;
44103  }
44104 
44106 
44114  const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
44115  get_append(axis,align).display(disp);
44116  return *this;
44117  }
44118 
44120 
44129  const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
44130  const char axis='x', const float align=0,
44131  unsigned int *const XYZ=0) const {
44132  bool is_exit = false;
44133  return _display(disp,0,display_info,axis,align,XYZ,0,true,is_exit);
44134  }
44135 
44137 
44143  const CImgList<T>& display(const char *const title=0, const bool display_info=true,
44144  const char axis='x', const float align=0,
44145  unsigned int *const XYZ=0) const {
44146  CImgDisplay disp;
44147  bool is_exit = false;
44148  return _display(disp,title,display_info,axis,align,XYZ,0,true,is_exit);
44149  }
44150 
44151  const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
44152  const char axis, const float align, unsigned int *const XYZ,
44153  const unsigned int orig, const bool is_first_call, bool &is_exit) const {
44154  if (is_empty())
44155  throw CImgInstanceException(_cimglist_instance
44156  "display(): Empty instance.",
44157  cimglist_instance);
44158  if (!disp) {
44159  if (axis=='x') {
44160  unsigned int sum_width = 0, max_height = 0;
44161  cimglist_for(*this,l) {
44162  const CImg<T> &img = _data[l];
44163  const unsigned int
44164  w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
44165  h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
44166  sum_width+=w;
44167  if (h>max_height) max_height = h;
44168  }
44169  disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
44170  } else {
44171  unsigned int max_width = 0, sum_height = 0;
44172  cimglist_for(*this,l) {
44173  const CImg<T> &img = _data[l];
44174  const unsigned int
44175  w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
44176  h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
44177  if (w>max_width) max_width = w;
44178  sum_height+=h;
44179  }
44180  disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
44181  }
44182  if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
44183  } else if (title) disp.set_title("%s",title);
44184  const CImg<char> dtitle = CImg<char>::string(disp.title());
44185  if (display_info) print(disp.title());
44186  disp.show().flush();
44187 
44188  if (_width==1) {
44189  const unsigned int dw = disp._width, dh = disp._height;
44190  if (!is_first_call)
44191  disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false).
44192  set_title("%s (%ux%ux%ux%u)",dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
44193  _data[0]._display(disp,0,false,XYZ,!is_first_call);
44194  if (disp.key()) is_exit = true;
44195  disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
44196  } else {
44197  bool disp_resize = !is_first_call;
44198  while (!disp.is_closed() && !is_exit) {
44199  const CImg<intT> s = _get_select(disp,0,true,axis,align,orig,disp_resize,!is_first_call,true);
44200  disp_resize = true;
44201  if (s[0]<0) { // No selections done.
44202  if (disp.button()&2) { disp.flush(); break; }
44203  is_exit = true;
44204  } else if (disp.wheel()) { // Zoom in/out.
44205  const int wheel = disp.wheel();
44206  disp.set_wheel();
44207  if (!is_first_call && wheel<0) break;
44208  if (wheel>0 && _width>=4) {
44209  const unsigned int
44210  delta = cimg::max(1U,(unsigned int)cimg::round(0.3*_width)),
44211  ind0 = (unsigned int)cimg::max(0,s[0] - (int)delta),
44212  ind1 = (unsigned int)cimg::min(width() - 1,s[0] + (int)delta);
44213  if ((ind0!=0 || ind1!=_width-1) && ind1 - ind0>=3) get_shared_images(ind0,ind1)._display(disp,0,false,axis,align,XYZ,orig + ind0,false,is_exit);
44214  }
44215  } else if (s[0]!=0 || s[1]!=width()-1) get_shared_images(s[0],s[1])._display(disp,0,false,axis,align,XYZ,orig+s[0],false,is_exit);
44216  }
44217  }
44218  return *this;
44219  }
44220 
44222 
44226  const CImgList<T>& save(const char *const filename, const int number=-1) const {
44227  if (!filename)
44228  throw CImgArgumentException(_cimglist_instance
44229  "save(): Specified filename is (null).",
44230  cimglist_instance);
44231  // Do not test for empty instances, since .cimg format is able to manage empty instances.
44232  const char *const ext = cimg::split_filename(filename);
44233  char nfilename[1024] = { 0 };
44234  const char *const fn = (number>=0)?cimg::number_filename(filename,number,6,nfilename):filename;
44235 #ifdef cimglist_save_plugin
44236  cimglist_save_plugin(fn);
44237 #endif
44238 #ifdef cimglist_save_plugin1
44239  cimglist_save_plugin1(fn);
44240 #endif
44241 #ifdef cimglist_save_plugin2
44242  cimglist_save_plugin2(fn);
44243 #endif
44244 #ifdef cimglist_save_plugin3
44245  cimglist_save_plugin3(fn);
44246 #endif
44247 #ifdef cimglist_save_plugin4
44248  cimglist_save_plugin4(fn);
44249 #endif
44250 #ifdef cimglist_save_plugin5
44251  cimglist_save_plugin5(fn);
44252 #endif
44253 #ifdef cimglist_save_plugin6
44254  cimglist_save_plugin6(fn);
44255 #endif
44256 #ifdef cimglist_save_plugin7
44257  cimglist_save_plugin7(fn);
44258 #endif
44259 #ifdef cimglist_save_plugin8
44260  cimglist_save_plugin8(fn);
44261 #endif
44262  if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
44263  else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
44264  else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,true);
44265  else if (!cimg::strcasecmp(ext,"avi") ||
44266  !cimg::strcasecmp(ext,"mov") ||
44267  !cimg::strcasecmp(ext,"asf") ||
44268  !cimg::strcasecmp(ext,"divx") ||
44269  !cimg::strcasecmp(ext,"flv") ||
44270  !cimg::strcasecmp(ext,"mpg") ||
44271  !cimg::strcasecmp(ext,"m1v") ||
44272  !cimg::strcasecmp(ext,"m2v") ||
44273  !cimg::strcasecmp(ext,"m4v") ||
44274  !cimg::strcasecmp(ext,"mjp") ||
44275  !cimg::strcasecmp(ext,"mkv") ||
44276  !cimg::strcasecmp(ext,"mpe") ||
44277  !cimg::strcasecmp(ext,"movie") ||
44278  !cimg::strcasecmp(ext,"ogm") ||
44279  !cimg::strcasecmp(ext,"ogg") ||
44280  !cimg::strcasecmp(ext,"qt") ||
44281  !cimg::strcasecmp(ext,"rm") ||
44282  !cimg::strcasecmp(ext,"vob") ||
44283  !cimg::strcasecmp(ext,"wmv") ||
44284  !cimg::strcasecmp(ext,"xvid") ||
44285  !cimg::strcasecmp(ext,"mpeg")) return save_ffmpeg(fn);
44286 #ifdef cimg_use_tiff
44287  else if (!cimg::strcasecmp(ext,"tif") ||
44288  !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
44289 #endif
44290  else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
44291  else { if (_width==1) _data[0].save(fn,-1); else cimglist_for(*this,l) _data[l].save(fn,l); }
44292  return *this;
44293  }
44294 
44296 
44300  static bool is_saveable(const char *const filename) {
44301  const char *const ext = cimg::split_filename(filename);
44302  if (!cimg::strcasecmp(ext,"cimgz") ||
44303 #ifdef cimg_use_tiff
44304  !cimg::strcasecmp(ext,"tif") ||
44305  !cimg::strcasecmp(ext,"tiff") ||
44306 #endif
44307  !cimg::strcasecmp(ext,"yuv") ||
44308  !cimg::strcasecmp(ext,"avi") ||
44309  !cimg::strcasecmp(ext,"mov") ||
44310  !cimg::strcasecmp(ext,"asf") ||
44311  !cimg::strcasecmp(ext,"divx") ||
44312  !cimg::strcasecmp(ext,"flv") ||
44313  !cimg::strcasecmp(ext,"mpg") ||
44314  !cimg::strcasecmp(ext,"m1v") ||
44315  !cimg::strcasecmp(ext,"m2v") ||
44316  !cimg::strcasecmp(ext,"m4v") ||
44317  !cimg::strcasecmp(ext,"mjp") ||
44318  !cimg::strcasecmp(ext,"mkv") ||
44319  !cimg::strcasecmp(ext,"mpe") ||
44320  !cimg::strcasecmp(ext,"movie") ||
44321  !cimg::strcasecmp(ext,"ogm") ||
44322  !cimg::strcasecmp(ext,"ogg") ||
44323  !cimg::strcasecmp(ext,"qt") ||
44324  !cimg::strcasecmp(ext,"rm") ||
44325  !cimg::strcasecmp(ext,"vob") ||
44326  !cimg::strcasecmp(ext,"wmv") ||
44327  !cimg::strcasecmp(ext,"xvid") ||
44328  !cimg::strcasecmp(ext,"mpeg")) return true;
44329  return false;
44330  }
44331 
44333 
44338  const CImgList<T>& save_gif_external(const char *const filename, const unsigned int fps=25, const unsigned int nb_loops=0) {
44339  char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
44340  CImgList<charT> filenames;
44341  std::FILE *file = 0;
44342 
44343 #ifdef cimg_use_png
44344 #define _cimg_save_gif_ext "png"
44345 #else
44346 #define _cimg_save_gif_ext "ppm"
44347 #endif
44348 
44349  do {
44350  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
44351  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001." _cimg_save_gif_ext,filetmp);
44352  if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
44353  } while (file);
44354  cimglist_for(*this,l) {
44355  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u." _cimg_save_gif_ext,filetmp,l+1);
44356  CImg<charT>::string(filetmp2).move_to(filenames);
44357  if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save(filetmp2);
44358  else _data[l].save(filetmp2);
44359  }
44360 
44361 #if cimg_OS!=2
44362  cimg_snprintf(command,sizeof(command),"%s -delay 1x%u -loop %u",
44363  cimg::imagemagick_path(),fps,nb_loops);
44364  CImg<ucharT>::string(command).move_to(filenames,0);
44365  cimg_snprintf(command,sizeof(command),"\"%s\" >/dev/null 2>&1",
44366  filename);
44367  CImg<ucharT>::string(command).move_to(filenames);
44368 #else
44369  cimg_snprintf(command,sizeof(command),"\"%s -delay 1x%u -loop %u",
44370  cimg::imagemagick_path(),fps,nb_loops);
44371  CImg<ucharT>::string(command).move_to(filenames,0);
44372  cimg_snprintf(command,sizeof(command),"\"%s\"\" >NUL 2>&1",
44373  filename);
44374  CImg<ucharT>::string(command).move_to(filenames);
44375 #endif
44376  CImg<charT> _command = filenames>'x';
44377  cimg_for(_command,p,char) if (!*p) *p = ' ';
44378  _command.back() = 0;
44379  cimg::system(_command);
44380  file = std::fopen(filename,"rb");
44381  if (!file)
44382  throw CImgIOException(_cimglist_instance
44383  "save_gif_external(): Failed to save file '%s' with external command 'convert'.",
44384  cimglist_instance,
44385  filename);
44386  else cimg::fclose(file);
44387  cimglist_for_in(*this,1,filenames._width-2,l) std::remove(filenames[l]);
44388  return *this;
44389  }
44390 
44392 
44397  // This piece of code has been originally written by David. G. Starkweather.
44398  const CImgList<T>& save_ffmpeg(const char *const filename, const unsigned int fps=25, const unsigned int bitrate=2048) const {
44399  if (!filename)
44400  throw CImgArgumentException(_cimglist_instance
44401  "save_ffmpeg(): Specified filename is (null).",
44402  cimglist_instance);
44403  if (is_empty())
44404  throw CImgInstanceException(_cimglist_instance
44405  "save_ffmpeg(): Empty instance, for file '%s'.",
44406  cimglist_instance,
44407  filename);
44408  if (!fps)
44409  throw CImgArgumentException(_cimglist_instance
44410  "save_ffmpeg(): Invalid specified framerate 0, for file '%s'.",
44411  cimglist_instance,
44412  filename);
44413 
44414  cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
44415  throw CImgInstanceException(_cimglist_instance
44416  "save_ffmpeg(): Invalid instance dimensions, for file '%s'.",
44417  cimglist_instance,
44418  filename);
44419 
44420 #ifndef cimg_use_ffmpeg
44421  return save_ffmpeg_external(filename,0,fps,bitrate);
44422 #else
44423  avcodec_register_all();
44424  av_register_all();
44425  const int
44426  frame_dimx = _data[0].width(),
44427  frame_dimy = _data[0].height(),
44428  frame_dimv = _data[0].spectrum();
44429  if (frame_dimv!=1 && frame_dimv!=3)
44430  throw CImgInstanceException(_cimglist_instance
44431  "save_ffmpeg(): Image[0] (%u,%u,%u,%u,%p) has not 1 or 3 channels, for file '%s'.",
44432  cimglist_instance,
44433  _data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum,_data,filename);
44434 
44435  PixelFormat dest_pxl_fmt = PIX_FMT_YUV420P;
44436  PixelFormat src_pxl_fmt = (frame_dimv==3)?PIX_FMT_RGB24:PIX_FMT_GRAY8;
44437 
44438  int sws_flags = SWS_FAST_BILINEAR; // Interpolation method (keeping same size images for now).
44439  AVOutputFormat *fmt = 0;
44440 #if defined(AV_VERSION_INT)
44441 #if LIBAVFORMAT_VERSION_INT<AV_VERSION_INT(52,45,0)
44442  fmt = guess_format(0,filename,0);
44443  if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg".
44444 #else
44445  fmt = av_guess_format(0,filename,0);
44446  if (!fmt) fmt = av_guess_format("mpeg",0,0); // Default format "mpeg".
44447 #endif
44448 #else
44449  fmt = guess_format(0,filename,0);
44450  if (!fmt) fmt = guess_format("mpeg",0,0); // Default format "mpeg".
44451 #endif
44452 
44453  if (!fmt)
44454  throw CImgArgumentException(_cimglist_instance
44455  "save_ffmpeg(): Unable to determine codec for file '%s'.",
44456  cimglist_instance,
44457  filename);
44458 
44459  AVFormatContext *oc = 0;
44460 #if defined(AV_VERSION_INT)
44461 #if LIBAVFORMAT_VERSION_INT<AV_VERSION_INT(52,36,0)
44462  oc = av_alloc_format_context();
44463 #else
44464  oc = avformat_alloc_context();
44465 #endif
44466 #else
44467  oc = av_alloc_format_context();
44468 #endif
44469  if (!oc) // Failed to allocate format context.
44470  throw CImgIOException(_cimglist_instance
44471  "save_ffmpeg(): Failed to allocate FFMPEG structure for format context, for file '%s'.",
44472  cimglist_instance,
44473  filename);
44474 
44475  AVCodec *codec = 0;
44476  AVFrame *picture = 0;
44477  AVFrame *tmp_pict = 0;
44478  oc->oformat = fmt;
44479  std::sprintf(oc->filename,"%s",filename);
44480 
44481  // Add video stream.
44482  int stream_index = 0;
44483  AVStream *video_str = 0;
44484  if (fmt->video_codec!=CODEC_ID_NONE) {
44485  video_str = av_new_stream(oc,stream_index);
44486  if (!video_str) { // Failed to allocate stream.
44487  av_free(oc);
44488  throw CImgIOException(_cimglist_instance
44489  "save_ffmpeg(): Failed to allocate FFMPEG structure for video stream, for file '%s'.",
44490  cimglist_instance,
44491  filename);
44492  }
44493  } else { // No codec identified.
44494  av_free(oc);
44495  throw CImgIOException(_cimglist_instance
44496  "save_ffmpeg(): Failed to identify proper codec, for file '%s'.",
44497  cimglist_instance,
44498  filename);
44499  }
44500 
44501  AVCodecContext *c = video_str->codec;
44502  c->codec_id = fmt->video_codec;
44503  c->codec_type = CODEC_TYPE_VIDEO;
44504  c->bit_rate = 1024*bitrate;
44505  c->width = frame_dimx;
44506  c->height = frame_dimy;
44507  c->time_base.num = 1;
44508  c->time_base.den = fps;
44509  c->gop_size = 12;
44510  c->pix_fmt = dest_pxl_fmt;
44511  if (c->codec_id==CODEC_ID_MPEG2VIDEO) c->max_b_frames = 2;
44512  if (c->codec_id==CODEC_ID_MPEG1VIDEO) c->mb_decision = 2;
44513 
44514  if (av_set_parameters(oc,0)<0) { // Parameters not properly set.
44515  av_free(oc);
44516  throw CImgIOException(_cimglist_instance
44517  "save_ffmpeg(): Invalid parameters set for avcodec, for file '%s'.",
44518  cimglist_instance,
44519  filename);
44520  }
44521 
44522  // Open codecs and alloc buffers.
44523  codec = avcodec_find_encoder(c->codec_id);
44524  if (!codec) { // Failed to find codec.
44525  av_free(oc);
44526  throw CImgIOException(_cimglist_instance
44527  "save_ffmpeg(): No valid codec found for file '%s'.",
44528  cimglist_instance,
44529  filename);
44530  }
44531  if (avcodec_open(c,codec)<0) // Failed to open codec.
44532  throw CImgIOException(_cimglist_instance
44533  "save_ffmpeg(): Failed to open codec for file '%s'.",
44534  cimglist_instance,
44535  filename);
44536 
44537  tmp_pict = avcodec_alloc_frame();
44538  if (!tmp_pict) { // Failed to allocate memory for tmp_pict frame.
44539  avcodec_close(video_str->codec);
44540  av_free(oc);
44541  throw CImgIOException(_cimglist_instance
44542  "save_ffmpeg(): Failed to allocate memory for file '%s'.",
44543  cimglist_instance,
44544  filename);
44545  }
44546  tmp_pict->linesize[0] = (src_pxl_fmt==PIX_FMT_RGB24)?3*frame_dimx:frame_dimx;
44547  tmp_pict->type = FF_BUFFER_TYPE_USER;
44548  int tmp_size = avpicture_get_size(src_pxl_fmt,frame_dimx,frame_dimy);
44549  uint8_t *tmp_buffer = (uint8_t*)av_malloc(tmp_size);
44550  if (!tmp_buffer) { // Failed to allocate memory for tmp buffer.
44551  av_free(tmp_pict);
44552  avcodec_close(video_str->codec);
44553  av_free(oc);
44554  throw CImgIOException(_cimglist_instance
44555  "save_ffmpeg(): Failed to allocate memory for file '%s'.",
44556  cimglist_instance,
44557  filename);
44558  }
44559 
44560  // Associate buffer with tmp_pict.
44561  avpicture_fill((AVPicture*)tmp_pict,tmp_buffer,src_pxl_fmt,frame_dimx,frame_dimy);
44562  picture = avcodec_alloc_frame();
44563  if (!picture) { // Failed to allocate picture frame.
44564  av_free(tmp_pict->data[0]);
44565  av_free(tmp_pict);
44566  avcodec_close(video_str->codec);
44567  av_free(oc);
44568  throw CImgIOException(_cimglist_instance
44569  "save_ffmpeg(): Failed to allocate memory for file '%s'.",
44570  cimglist_instance,
44571  filename);
44572  }
44573 
44574  int size = avpicture_get_size(c->pix_fmt,frame_dimx,frame_dimy);
44575  uint8_t *buffer = (uint8_t*)av_malloc(size);
44576  if (!buffer) { // Failed to allocate picture frame buffer.
44577  av_free(picture);
44578  av_free(tmp_pict->data[0]);
44579  av_free(tmp_pict);
44580  avcodec_close(video_str->codec);
44581  av_free(oc);
44582  throw CImgIOException(_cimglist_instance
44583  "save_ffmpeg(): Failed to allocate memory for file '%s'.",
44584  cimglist_instance,
44585  filename);
44586  }
44587 
44588  // Associate the buffer with picture.
44589  avpicture_fill((AVPicture*)picture,buffer,c->pix_fmt,frame_dimx,frame_dimy);
44590 
44591  // Open file.
44592  if (!(fmt->flags&AVFMT_NOFILE)) {
44593  if (url_fopen(&oc->pb,filename,URL_WRONLY)<0)
44594  throw CImgIOException(_cimglist_instance
44595  "save_ffmpeg(): Failed to open file '%s'.",
44596  cimglist_instance,
44597  filename);
44598  }
44599 
44600  if (av_write_header(oc)<0)
44601  throw CImgIOException(_cimglist_instance
44602  "save_ffmpeg(): Failed to write header in file '%s'.",
44603  cimglist_instance,
44604  filename);
44605 
44606  SwsContext *img_convert_context = 0;
44607  img_convert_context = sws_getContext(frame_dimx,frame_dimy,src_pxl_fmt,
44608  c->width,c->height,c->pix_fmt,sws_flags,0,0,0);
44609  if (!img_convert_context) { // Failed to get swscale context.
44610  // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
44611  av_free(picture->data);
44612  av_free(picture);
44613  av_free(tmp_pict->data[0]);
44614  av_free(tmp_pict);
44615  avcodec_close(video_str->codec);
44616  av_free(oc);
44617  throw CImgIOException(_cimglist_instance
44618  "save_ffmpeg(): Failed to get conversion context for file '%s'.",
44619  cimglist_instance,
44620  filename);
44621  }
44622  int ret = 0, out_size;
44623  uint8_t *video_outbuf = 0;
44624  int video_outbuf_size = 1000000;
44625  video_outbuf = (uint8_t*)av_malloc(video_outbuf_size);
44626  if (!video_outbuf) {
44627  // if (!(fmt->flags & AVFMT_NOFILE)) url_fclose(&oc->pb);
44628  av_free(picture->data);
44629  av_free(picture);
44630  av_free(tmp_pict->data[0]);
44631  av_free(tmp_pict);
44632  avcodec_close(video_str->codec);
44633  av_free(oc);
44634  throw CImgIOException(_cimglist_instance
44635  "save_ffmpeg(): Failed to allocate memory, for file '%s'.",
44636  cimglist_instance,
44637  filename);
44638  }
44639 
44640  // Loop through each desired image in list.
44641  cimglist_for(*this,i) {
44642  CImg<uint8_t> currentIm = _data[i], red, green, blue, gray;
44643  if (src_pxl_fmt==PIX_FMT_RGB24) {
44644  red = currentIm.get_shared_channel(0);
44645  green = currentIm.get_shared_channel(1);
44646  blue = currentIm.get_shared_channel(2);
44647  cimg_forXY(currentIm,X,Y) { // Assign pizel values to data buffer in interlaced RGBRGB ... format.
44648  tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X] = red(X,Y);
44649  tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 1] = green(X,Y);
44650  tmp_pict->data[0][Y*tmp_pict->linesize[0] + 3*X + 2] = blue(X,Y);
44651  }
44652  } else {
44653  gray = currentIm.get_shared_channel(0);
44654  cimg_forXY(currentIm,X,Y) tmp_pict->data[0][Y*tmp_pict->linesize[0] + X] = gray(X,Y);
44655  }
44656  if (!video_str) break;
44657  if (sws_scale(img_convert_context,tmp_pict->data,tmp_pict->linesize,0,c->height,picture->data,picture->linesize)<0) break;
44658  out_size = avcodec_encode_video(c,video_outbuf,video_outbuf_size,picture);
44659  if (out_size>0) {
44660  AVPacket pkt;
44661  av_init_packet(&pkt);
44662  pkt.pts = av_rescale_q(c->coded_frame->pts,c->time_base,video_str->time_base);
44663  if (c->coded_frame->key_frame) pkt.flags|=PKT_FLAG_KEY;
44664  pkt.stream_index = video_str->index;
44665  pkt.data = video_outbuf;
44666  pkt.size = out_size;
44667  ret = av_write_frame(oc,&pkt);
44668  } else if (out_size<0) break;
44669  if (ret) break; // Error occured in writing frame.
44670  }
44671 
44672  // Close codec.
44673  if (video_str) {
44674  avcodec_close(video_str->codec);
44675  av_free(picture->data[0]);
44676  av_free(picture);
44677  av_free(tmp_pict->data[0]);
44678  av_free(tmp_pict);
44679  }
44680  if (av_write_trailer(oc)<0)
44681  throw CImgIOException(_cimglist_instance
44682  "save_ffmpeg(): Failed to write trailer for file '%s'.",
44683  cimglist_instance,
44684  filename);
44685 
44686  av_freep(&oc->streams[stream_index]->codec);
44687  av_freep(&oc->streams[stream_index]);
44688  if (!(fmt->flags&AVFMT_NOFILE)) {
44689  /*if (url_fclose(oc->pb)<0)
44690  throw CImgIOException(_cimglist_instance
44691  "save_ffmpeg(): File '%s', failed to close file.",
44692  cimglist_instance,
44693  filename);
44694  */
44695  }
44696  av_free(oc);
44697  av_free(video_outbuf);
44698 #endif
44699  return *this;
44700  }
44701 
44702  const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename, const bool is_rgb) const {
44703  if (!file && !filename)
44704  throw CImgArgumentException(_cimglist_instance
44705  "save_yuv(): Specified filename is (null).",
44706  cimglist_instance);
44707  if (is_empty())
44708  throw CImgInstanceException(_cimglist_instance
44709  "save_yuv(): Empty instance, for file '%s'.",
44710  cimglist_instance,
44711  filename?filename:"(FILE*)");
44712 
44713  if ((*this)[0].width()%2 || (*this)[0].height()%2)
44714  throw CImgInstanceException(_cimglist_instance
44715  "save_yuv(): Invalid odd instance dimensions (%u,%u) for file '%s'.",
44716  cimglist_instance,
44717  (*this)[0].width(),(*this)[0].height(),
44718  filename?filename:"(FILE*)");
44719 
44720  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
44721  cimglist_for(*this,l) {
44722  CImg<ucharT> YCbCr((*this)[l]);
44723  if (is_rgb) YCbCr.RGBtoYCbCr();
44724  cimg::fwrite(YCbCr._data,(unsigned long)YCbCr._width*YCbCr._height,nfile);
44725  cimg::fwrite(YCbCr.get_resize(YCbCr._width/2, YCbCr._height/2,1,3,3).data(0,0,0,1),
44726  (unsigned long)YCbCr._width*YCbCr._height/2,nfile);
44727  }
44728  if (!file) cimg::fclose(nfile);
44729  return *this;
44730  }
44731 
44733 
44737  const CImgList<T>& save_yuv(const char *const filename=0, const bool is_rgb=true) const {
44738  return _save_yuv(0,filename,is_rgb);
44739  }
44740 
44742 
44746  const CImgList<T>& save_yuv(std::FILE *const file, const bool is_rgb=true) const {
44747  return _save_yuv(file,0,is_rgb);
44748  }
44749 
44750  const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
44751  if (!file && !filename)
44752  throw CImgArgumentException(_cimglist_instance
44753  "save_cimg(): Specified filename is (null).",
44754  cimglist_instance);
44755 #ifndef cimg_use_zlib
44756  if (is_compressed)
44757  cimg::warn(_cimglist_instance
44758  "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, saving them uncompressed.",
44759  cimglist_instance,
44760  filename?filename:"(FILE*)");
44761 #endif
44762  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
44763  const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
44764  if (std::strstr(ptype,"unsigned")==ptype) std::fprintf(nfile,"%u unsigned_%s %s_endian\n",_width,ptype+9,etype);
44765  else std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
44766  cimglist_for(*this,l) {
44767  const CImg<T>& img = _data[l];
44768  std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
44769  if (img._data) {
44770  CImg<T> tmp;
44771  if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
44772  const CImg<T>& ref = cimg::endianness()?tmp:img;
44773  bool failed_to_compress = true;
44774  if (is_compressed) {
44775 #ifdef cimg_use_zlib
44776  const unsigned long siz = sizeof(T)*ref.size();
44777  unsigned long csiz = siz + siz/100 + 16;
44778  Bytef *const cbuf = new Bytef[csiz];
44779  if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
44780  cimg::warn(_cimglist_instance
44781  "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
44782  cimglist_instance,
44783  filename?filename:"(FILE*)");
44784  else {
44785  std::fprintf(nfile," #%lu\n",csiz);
44786  cimg::fwrite(cbuf,csiz,nfile);
44787  delete[] cbuf;
44788  failed_to_compress = false;
44789  }
44790 #endif
44791  }
44792  if (failed_to_compress) { // Write in a non-compressed way.
44793  std::fputc('\n',nfile);
44794  cimg::fwrite(ref._data,ref.size(),nfile);
44795  }
44796  } else std::fputc('\n',nfile);
44797  }
44798  if (!file) cimg::fclose(nfile);
44799  return *this;
44800  }
44801 
44803 
44807  const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
44808  return _save_cimg(0,filename,is_compressed);
44809  }
44810 
44812 
44816  const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
44817  return _save_cimg(file,0,is_compressed);
44818  }
44819 
44820  const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
44821  const unsigned int n0,
44822  const unsigned int x0, const unsigned int y0,
44823  const unsigned int z0, const unsigned int c0) const {
44824 #define _cimg_save_cimg_case(Ts,Tss) \
44825  if (!saved && !cimg::strcasecmp(Ts,str_pixeltype)) { \
44826  for (unsigned int l = 0; l<lmax; ++l) { \
44827  j = 0; while((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
44828  W = H = D = C = 0; \
44829  if (std::sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
44830  throw CImgIOException(_cimglist_instance \
44831  "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
44832  cimglist_instance, \
44833  W,H,D,C,l,filename?filename:"(FILE*)"); \
44834  if (W*H*D*C>0) { \
44835  if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) std::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
44836  else { \
44837  const CImg<T>& img = (*this)[l - n0]; \
44838  const T *ptrs = img._data; \
44839  const unsigned int \
44840  x1 = x0 + img._width - 1, \
44841  y1 = y0 + img._height - 1, \
44842  z1 = z0 + img._depth - 1, \
44843  c1 = c0 + img._spectrum - 1, \
44844  nx1 = x1>=W?W-1:x1, \
44845  ny1 = y1>=H?H-1:y1, \
44846  nz1 = z1>=D?D-1:z1, \
44847  nc1 = c1>=C?C-1:c1; \
44848  CImg<Tss> raw(1+nx1-x0); \
44849  const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
44850  if (skipvb) std::fseek(nfile,skipvb,SEEK_CUR); \
44851  for (unsigned int v = 1 + nc1 - c0; v; --v) { \
44852  const unsigned int skipzb = z0*W*H*sizeof(Tss); \
44853  if (skipzb) std::fseek(nfile,skipzb,SEEK_CUR); \
44854  for (unsigned int z = 1 + nz1 - z0; z; --z) { \
44855  const unsigned int skipyb = y0*W*sizeof(Tss); \
44856  if (skipyb) std::fseek(nfile,skipyb,SEEK_CUR); \
44857  for (unsigned int y = 1 + ny1 - y0; y; --y) { \
44858  const unsigned int skipxb = x0*sizeof(Tss); \
44859  if (skipxb) std::fseek(nfile,skipxb,SEEK_CUR); \
44860  raw.assign(ptrs, raw._width); \
44861  ptrs+=img._width; \
44862  if (endian) cimg::invert_endianness(raw._data,raw._width); \
44863  cimg::fwrite(raw._data,raw._width,nfile); \
44864  const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
44865  if (skipxe) std::fseek(nfile,skipxe,SEEK_CUR); \
44866  } \
44867  const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
44868  if (skipye) std::fseek(nfile,skipye,SEEK_CUR); \
44869  } \
44870  const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
44871  if (skipze) std::fseek(nfile,skipze,SEEK_CUR); \
44872  } \
44873  const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
44874  if (skipve) std::fseek(nfile,skipve,SEEK_CUR); \
44875  } \
44876  } \
44877  } \
44878  saved = true; \
44879  }
44880 
44881  if (!file && !filename)
44882  throw CImgArgumentException(_cimglist_instance
44883  "save_cimg(): Specified filename is (null).",
44884  cimglist_instance);
44885  if (is_empty())
44886  throw CImgInstanceException(_cimglist_instance
44887  "save_cimg(): Empty instance, for file '%s'.",
44888  cimglist_instance,
44889  filename?filename:"(FILE*)");
44890 
44891  std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
44892  bool saved = false, endian = cimg::endianness();
44893  char tmp[256] = { 0 }, str_pixeltype[256] = { 0 }, str_endian[256] = { 0 };
44894  unsigned int j, err, N, W, H, D, C;
44895  int i;
44896  j = 0; while((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
44897  err = std::sscanf(tmp,"%u%*c%255[A-Za-z_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype,str_endian);
44898  if (err<2) {
44899  if (!file) cimg::fclose(nfile);
44900  throw CImgIOException(_cimglist_instance
44901  "save_cimg(): CImg header not found in file '%s'.",
44902  cimglist_instance,
44903  filename?filename:"(FILE*)");
44904  }
44905  if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
44906  else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
44907  const unsigned int lmax = cimg::min(N,n0+_width);
44908  _cimg_save_cimg_case("bool",bool);
44909  _cimg_save_cimg_case("unsigned_char",unsigned char);
44910  _cimg_save_cimg_case("uchar",unsigned char);
44911  _cimg_save_cimg_case("char",char);
44912  _cimg_save_cimg_case("unsigned_short",unsigned short);
44913  _cimg_save_cimg_case("ushort",unsigned short);
44914  _cimg_save_cimg_case("short",short);
44915  _cimg_save_cimg_case("unsigned_int",unsigned int);
44916  _cimg_save_cimg_case("uint",unsigned int);
44917  _cimg_save_cimg_case("int",int);
44918  _cimg_save_cimg_case("unsigned_long",unsigned long);
44919  _cimg_save_cimg_case("ulong",unsigned long);
44920  _cimg_save_cimg_case("long",long);
44921  _cimg_save_cimg_case("float",float);
44922  _cimg_save_cimg_case("double",double);
44923  if (!saved) {
44924  if (!file) cimg::fclose(nfile);
44925  throw CImgIOException(_cimglist_instance
44926  "save_cimg(): Unsupported data type '%s' for file '%s'.",
44927  cimglist_instance,
44928  filename?filename:"(FILE*)",str_pixeltype);
44929  }
44930  if (!file) cimg::fclose(nfile);
44931  return *this;
44932  }
44933 
44935 
44943  const CImgList<T>& save_cimg(const char *const filename,
44944  const unsigned int n0,
44945  const unsigned int x0, const unsigned int y0,
44946  const unsigned int z0, const unsigned int c0) const {
44947  return _save_cimg(0,filename,n0,x0,y0,z0,c0);
44948  }
44949 
44951 
44959  const CImgList<T>& save_cimg(std::FILE *const file,
44960  const unsigned int n0,
44961  const unsigned int x0, const unsigned int y0,
44962  const unsigned int z0, const unsigned int c0) const {
44963  return _save_cimg(file,0,n0,x0,y0,z0,c0);
44964  }
44965 
44966  static void _save_empty_cimg(std::FILE *const file, const char *const filename,
44967  const unsigned int nb,
44968  const unsigned int dx, const unsigned int dy,
44969  const unsigned int dz, const unsigned int dc) {
44970  std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
44971  const unsigned long siz = (unsigned long)dx*dy*dz*dc*sizeof(T);
44972  std::fprintf(nfile,"%u %s\n",nb,pixel_type());
44973  for (unsigned int i=nb; i; --i) {
44974  std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
44975  for (unsigned long off=siz; off; --off) std::fputc(0,nfile);
44976  }
44977  if (!file) cimg::fclose(nfile);
44978  }
44979 
44981 
44989  static void save_empty_cimg(const char *const filename,
44990  const unsigned int nb,
44991  const unsigned int dx, const unsigned int dy=1,
44992  const unsigned int dz=1, const unsigned int dc=1) {
44993  return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
44994  }
44995 
44997 
45005  static void save_empty_cimg(std::FILE *const file,
45006  const unsigned int nb,
45007  const unsigned int dx, const unsigned int dy=1,
45008  const unsigned int dz=1, const unsigned int dc=1) {
45009  return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
45010  }
45011 
45013 
45017  const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0) const {
45018  if (!filename)
45019  throw CImgArgumentException(_cimglist_instance
45020  "save_tiff(): Specified filename is (null).",
45021  cimglist_instance);
45022  if (is_empty())
45023  throw CImgInstanceException(_cimglist_instance
45024  "save_tiff(): Empty instance, for file '%s'.",
45025  cimglist_instance,
45026  filename);
45027 #ifndef cimg_use_tiff
45028  if (_width==1) _data[0].save_tiff(filename,compression_type);
45029  else cimglist_for(*this,l) {
45030  char nfilename[1024] = { 0 };
45031  cimg::number_filename(filename,l,6,nfilename);
45032  _data[l].save_tiff(nfilename,compression_type);
45033  }
45034 #else
45035  TIFF *tif = TIFFOpen(filename,"w");
45036  if (tif) {
45037  for (unsigned int dir = 0, l = 0; l<_width; ++l) {
45038  const CImg<T>& img = (*this)[l];
45039  if (img) {
45040  if (img._depth==1) img._save_tiff(tif,dir++,compression_type);
45041  else cimg_forZ(img,z) img.get_slice(z)._save_tiff(tif,dir++,compression_type);
45042  }
45043  }
45044  TIFFClose(tif);
45045  } else
45046  throw CImgIOException(_cimglist_instance
45047  "save_tiff(): Failed to open stream for file '%s'.",
45048  cimglist_instance,
45049  filename);
45050 #endif
45051  return *this;
45052  }
45053 
45054 
45056 
45059  const CImgList<T>& save_gzip_external(const char *const filename) const {
45060  if (!filename)
45061  throw CImgIOException(_cimglist_instance
45062  "save_gzip_external(): Specified filename is (null).",
45063  cimglist_instance);
45064 
45065  char command[1024] = { 0 }, filetmp[512] = { 0 }, body[512] = { 0 };
45066  const char
45067  *ext = cimg::split_filename(filename,body),
45068  *ext2 = cimg::split_filename(body,0);
45069  std::FILE *file;
45070  do {
45071  if (!cimg::strcasecmp(ext,"gz")) {
45072  if (*ext2) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
45073  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
45074  } else {
45075  if (*ext) cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
45076  else cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s.cimg",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
45077  }
45078  if ((file=std::fopen(filetmp,"rb"))!=0) cimg::fclose(file);
45079  } while (file);
45080 
45081  if (is_saveable(body)) {
45082  save(filetmp);
45083  cimg_snprintf(command,sizeof(command),"%s -c %s > \"%s\"",cimg::gzip_path(),filetmp,filename);
45084  cimg::system(command);
45085  file = std::fopen(filename,"rb");
45086  if (!file)
45087  throw CImgIOException(_cimglist_instance
45088  "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
45089  cimglist_instance,
45090  filename);
45091  else cimg::fclose(file);
45092  std::remove(filetmp);
45093  } else {
45094  char nfilename[1024] = { 0 };
45095  cimglist_for(*this,l) {
45096  cimg::number_filename(body,l,6,nfilename);
45097  if (*ext) std::sprintf(nfilename + std::strlen(nfilename),".%s",ext);
45098  _data[l].save_gzip_external(nfilename);
45099  }
45100  }
45101  return *this;
45102  }
45103 
45105 
45111  const CImgList<T>& save_ffmpeg_external(const char *const filename, const char *const codec=0,
45112  const unsigned int fps=25, const unsigned int bitrate=2048) const {
45113  if (!filename)
45114  throw CImgArgumentException(_cimglist_instance
45115  "save_ffmpeg_external(): Specified filename is (null).",
45116  cimglist_instance);
45117  if (is_empty())
45118  throw CImgInstanceException(_cimglist_instance
45119  "save_ffmpeg_external(): Empty instance, for file '%s'.",
45120  cimglist_instance,
45121  filename);
45122  const char
45123  *const ext = cimg::split_filename(filename),
45124  *const _codec = codec?codec:!cimg::strcasecmp(ext,"flv")?"flv":"mpeg2video";
45125 
45126  char command[1024] = { 0 }, filetmp[512] = { 0 }, filetmp2[512] = { 0 };
45127  CImgList<charT> filenames;
45128  std::FILE *file = 0;
45129  cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
45130  throw CImgInstanceException(_cimglist_instance
45131  "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
45132  cimglist_instance,
45133  filename);
45134  do {
45135  cimg_snprintf(filetmp,sizeof(filetmp),"%s%c%s",cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
45136  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_000001.ppm",filetmp);
45137  if ((file=std::fopen(filetmp2,"rb"))!=0) cimg::fclose(file);
45138  } while (file);
45139  cimglist_for(*this,l) {
45140  cimg_snprintf(filetmp2,sizeof(filetmp2),"%s_%.6u.ppm",filetmp,l+1);
45141  CImg<charT>::string(filetmp2).move_to(filenames);
45142  if (_data[l]._depth>1 || _data[l]._spectrum!=3) _data[l].get_resize(-100,-100,1,3).save_pnm(filetmp2);
45143  else _data[l].save_pnm(filetmp2);
45144  }
45145 #if cimg_OS!=2
45146  cimg_snprintf(command,sizeof(command),"%s -i %s_%%6d.ppm -vcodec %s -b %uk -r %u -y \"%s\" >/dev/null 2>&1",
45147  cimg::ffmpeg_path(),filetmp,_codec,bitrate,fps,filename);
45148 #else
45149  cimg_snprintf(command,sizeof(command),"\"%s -i %s_%%6d.ppm -vcodec %s -b %uk -r %u -y \"%s\"\" >NUL 2>&1",
45150  cimg::ffmpeg_path(),filetmp,_codec,bitrate,fps,filename);
45151 #endif
45152  cimg::system(command);
45153  file = std::fopen(filename,"rb");
45154  if (!file)
45155  throw CImgIOException(_cimglist_instance
45156  "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
45157  cimglist_instance,
45158  filename);
45159  else cimg::fclose(file);
45160  cimglist_for(*this,l) std::remove(filenames[l]);
45161  return *this;
45162  }
45163 
45165  //----------------------------------
45166  //
45168 
45169  //----------------------------------
45170 
45172 
45175  return get_crop_font().move_to(*this);
45176  }
45177 
45179 
45182  CImgList<T> res;
45183  cimglist_for(*this,l) {
45184  const CImg<T>& letter = (*this)[l];
45185  int xmin = letter._width, xmax = 0;
45186  cimg_forXY(letter,x,y) if (letter(x,y)) { if (x<xmin) xmin = x; if (x>xmax) xmax = x; }
45187  if (xmin>xmax) CImg<T>(letter._width,letter._height,1,letter._spectrum,0).move_to(res);
45188  else letter.get_crop(xmin,0,xmax,letter._height-1).move_to(res);
45189  }
45190  res[' '].resize(res['f']._width,-100,-100,-100,0);
45191  if (' '+256<res.size()) res[' '+256].resize(res['f']._width,-100,-100,-100,0);
45192  return res;
45193  }
45194 
45195 
45197 
45201  static const CImgList<T>& font(const unsigned int font_height, const bool is_variable_width=true) {
45202 
45203 #define _cimg_font(sx,sy) \
45204  if (!is_variable_width && (!font || font[0]._height!=sy)) font = _font(cimg::font##sx##x##sy,sx,sy,false); \
45205  if (is_variable_width && (!vfont || vfont[0]._height!=sy)) vfont = _font(cimg::font##sx##x##sy,sx,sy,true); \
45206  if (font_height==sy) return is_variable_width?vfont:font; \
45207  if (is_variable_width) { \
45208  if (cvfont && font_height==cvfont[0]._height) return cvfont; \
45209  cvfont = vfont; \
45210  cimglist_for(cvfont,l) \
45211  cvfont[l].resize(cimg::max(1U,cvfont[l]._width*font_height/cvfont[l]._height),font_height,-100,-100, \
45212  cvfont[0]._height>font_height?2:5); \
45213  return cvfont; \
45214  } else { \
45215  if (cfont && font_height==cfont[0]._height) return cfont; \
45216  cfont = font; \
45217  cimglist_for(cfont,l) \
45218  cfont[l].resize(cimg::max(1U,cfont[l]._width*font_height/cfont[l]._height),font_height,-100,-100, \
45219  cfont[0]._height>font_height?2:5); \
45220  return cfont; \
45221  } \
45222 
45223  static CImgList<T> font, vfont, cfont, cvfont;
45224  if (!font_height) return CImgList<T>::empty();
45225  if (font_height<=13) { _cimg_font(10,13); } // [1,13] -> ref 13
45226  if (font_height<=28) { _cimg_font(12,24); } // [14,28] -> ref 24
45227  if (font_height<=32) { _cimg_font(16,32); } // [29,32] -> ref 32
45228  _cimg_font(29,57); // [33,+inf] -> ref 57
45229  }
45230 
45231  static CImgList<T> _font(const unsigned int *const font, const unsigned int w, const unsigned int h, const bool is_variable_width) {
45232  CImgList<T> res(256,w,h,1,1);
45233  const unsigned int *ptr = font;
45234  unsigned int m = 0, val = 0;
45235  for (unsigned int y = 0; y<h; ++y)
45236  for (unsigned int x = 0; x<256*w; ++x) {
45237  m>>=1; if (!m) { m = 0x80000000; val = *(ptr++); }
45238  CImg<T>& img = res[x/w];
45239  unsigned int xm = x%w;
45240  img(xm,y) = (T)((val&m)?1:0);
45241  }
45242  if (is_variable_width) res.crop_font();
45243  return res.insert(res);
45244  }
45245 
45247 
45251  CImgList<T>& FFT(const char axis, const bool invert=false) {
45252  if (is_empty()) return *this;
45253  if (_width==1) insert(1);
45254  if (_width>2)
45255  cimg::warn(_cimglist_instance
45256  "FFT(): Instance has more than 2 images",
45257  cimglist_instance);
45258 
45259  CImg<T>::FFT(_data[0],_data[1],axis,invert);
45260  return *this;
45261  }
45262 
45264  CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
45265  return CImgList<Tfloat>(*this,false).FFT(axis,invert);
45266  }
45267 
45269 
45272  CImgList<T>& FFT(const bool invert=false) {
45273  if (is_empty()) return *this;
45274  if (_width==1) insert(1);
45275  if (_width>2)
45276  cimg::warn(_cimglist_instance
45277  "FFT(): Instance has more than 2 images",
45278  cimglist_instance);
45279 
45280  CImg<T>::FFT(_data[0],_data[1],invert);
45281  return *this;
45282  }
45283 
45285  CImgList<Tfloat> get_FFT(const bool invert=false) const {
45286  return CImgList<Tfloat>(*this,false).FFT(invert);
45287  }
45288 
45290 
45293  cimglist_for(*this,l) {
45294  CImg<T>& p = _data[l];
45295  switch (p.size()) {
45296  case 2: case 3: cimg::swap(p[0],p[1]); break;
45297  case 6: cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
45298  case 9: cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
45299  case 4: cimg::swap(p[0],p[1],p[2],p[3]); break;
45300  case 12: cimg::swap(p[0],p[1],p[2],p[3],p[4],p[6],p[5],p[7],p[8],p[10],p[9],p[11]); break;
45301  }
45302  }
45303  return *this;
45304  }
45305 
45308  return (+*this).reverse_object3d();
45309  }
45310 
45312  }; // struct CImgList<T> { ...
45313 
45314  /*
45315  #---------------------------------------------
45316  #
45317  # Completion of previously declared functions
45318  #
45319  #----------------------------------------------
45320  */
45321 
45322 namespace cimg {
45323 
45325 
45342  template<typename t>
45343  inline int dialog(const char *const title, const char *const msg,
45344  const char *const button1_label, const char *const button2_label,
45345  const char *const button3_label, const char *const button4_label,
45346  const char *const button5_label, const char *const button6_label,
45347  const CImg<t>& logo, const bool is_centered = false) {
45348 #if cimg_display==0
45349  cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,logo._data,is_centered);
45350  throw CImgIOException("cimg::dialog(): No display available.");
45351 #else
45352  const unsigned char
45353  black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
45354 
45355  // Create buttons and canvas graphics
45356  CImgList<unsigned char> buttons, cbuttons, sbuttons;
45357  if (button1_label) { CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
45358  if (button2_label) { CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
45359  if (button3_label) { CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
45360  if (button4_label) { CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
45361  if (button5_label) { CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
45362  if (button6_label) { CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
45363  }}}}}}
45364  if (!buttons._width)
45365  throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
45366  cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
45367 
45368  unsigned int bw = 0, bh = 0;
45369  cimglist_for(buttons,l) { bw = cimg::max(bw,buttons[l]._width); bh = cimg::max(bh,buttons[l]._height); }
45370  bw+=8; bh+=8;
45371  if (bw<64) bw = 64;
45372  if (bw>128) bw = 128;
45373  if (bh<24) bh = 24;
45374  if (bh>48) bh = 48;
45375 
45376  CImg<unsigned char> button(bw,bh,1,3);
45377  button.draw_rectangle(0,0,bw-1,bh-1,gray);
45378  button.draw_line(0,0,bw-1,0,white).draw_line(0,bh-1,0,0,white);
45379  button.draw_line(bw-1,0,bw-1,bh-1,black).draw_line(bw-1,bh-1,0,bh-1,black);
45380  button.draw_line(1,bh-2,bw-2,bh-2,gray2).draw_line(bw-2,bh-2,bw-2,1,gray2);
45381  CImg<unsigned char> sbutton(bw,bh,1,3);
45382  sbutton.draw_rectangle(0,0,bw-1,bh-1,gray);
45383  sbutton.draw_line(0,0,bw-1,0,black).draw_line(bw-1,0,bw-1,bh-1,black);
45384  sbutton.draw_line(bw-1,bh-1,0,bh-1,black).draw_line(0,bh-1,0,0,black);
45385  sbutton.draw_line(1,1,bw-2,1,white).draw_line(1,bh-2,1,1,white);
45386  sbutton.draw_line(bw-2,1,bw-2,bh-2,black).draw_line(bw-2,bh-2,1,bh-2,black);
45387  sbutton.draw_line(2,bh-3,bw-3,bh-3,gray2).draw_line(bw-3,bh-3,bw-3,2,gray2);
45388  sbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false);
45389  sbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false);
45390  CImg<unsigned char> cbutton(bw,bh,1,3);
45391  cbutton.draw_rectangle(0,0,bw-1,bh-1,black).draw_rectangle(1,1,bw-2,bh-2,gray2).draw_rectangle(2,2,bw-3,bh-3,gray);
45392  cbutton.draw_line(4,4,bw-5,4,black,1,0xAAAAAAAA,true).draw_line(bw-5,4,bw-5,bh-5,black,1,0xAAAAAAAA,false);
45393  cbutton.draw_line(bw-5,bh-5,4,bh-5,black,1,0xAAAAAAAA,false).draw_line(4,bh-5,4,4,black,1,0xAAAAAAAA,false);
45394 
45395  cimglist_for(buttons,ll) {
45396  CImg<unsigned char>(cbutton).draw_image(1+(bw-buttons[ll].width())/2,1+(bh-buttons[ll].height())/2,buttons[ll]).
45397  move_to(cbuttons);
45398  CImg<unsigned char>(sbutton).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
45399  move_to(sbuttons);
45400  CImg<unsigned char>(button).draw_image((bw-buttons[ll].width())/2,(bh-buttons[ll].height())/2,buttons[ll]).
45401  move_to(buttons[ll]);
45402  }
45403 
45404  CImg<unsigned char> canvas;
45405  if (msg) CImg<unsigned char>().draw_text(0,0,"%s",black,gray,1,13,msg).resize(-100,-100,1,3).move_to(canvas);
45406  const unsigned int
45407  bwall = (buttons._width-1)*(12+bw) + bw,
45408  w = cimg::max(196U,36+logo._width+canvas._width,24+bwall),
45409  h = cimg::max(96U,36+canvas._height+bh,36+logo._height+bh),
45410  lx = 12 + (canvas._data?0:((w-24-logo._width)/2)),
45411  ly = (h-12-bh-logo._height)/2,
45412  tx = lx+logo._width+12,
45413  ty = (h-12-bh-canvas._height)/2,
45414  bx = (w-bwall)/2,
45415  by = h-12-bh;
45416 
45417  if (canvas._data)
45418  canvas = CImg<unsigned char>(w,h,1,3).
45419  draw_rectangle(0,0,w-1,h-1,gray).
45420  draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
45421  draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black).
45422  draw_image(tx,ty,canvas);
45423  else
45424  canvas = CImg<unsigned char>(w,h,1,3).
45425  draw_rectangle(0,0,w-1,h-1,gray).
45426  draw_line(0,0,w-1,0,white).draw_line(0,h-1,0,0,white).
45427  draw_line(w-1,0,w-1,h-1,black).draw_line(w-1,h-1,0,h-1,black);
45428  if (logo._data) canvas.draw_image(lx,ly,logo);
45429 
45430  unsigned int xbuttons[6] = { 0 };
45431  cimglist_for(buttons,lll) { xbuttons[lll] = bx+(bw+12)*lll; canvas.draw_image(xbuttons[lll],by,buttons[lll]); }
45432 
45433  // Open window and enter events loop
45434  CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
45435  if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
45436  (CImgDisplay::screen_height() - disp.height())/2);
45437  bool stopflag = false, refresh = false;
45438  int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
45439  while (!disp.is_closed() && !stopflag) {
45440  if (refresh) {
45441  if (clicked>=0) CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
45442  else {
45443  if (selected>=0) CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
45444  else canvas.display(disp);
45445  }
45446  refresh = false;
45447  }
45448  disp.wait(15);
45449  if (disp.is_resized()) disp.resize(disp,false);
45450 
45451  if (disp.button()&1) {
45452  oclicked = clicked;
45453  clicked = -1;
45454  cimglist_for(buttons,l)
45455  if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by+bh) &&
45456  disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l]+bw)) {
45457  clicked = selected = l;
45458  refresh = true;
45459  }
45460  if (clicked!=oclicked) refresh = true;
45461  } else if (clicked>=0) stopflag = true;
45462 
45463  if (disp.key()) {
45464  oselected = selected;
45465  switch (disp.key()) {
45466  case cimg::keyESC : selected=-1; stopflag=true; break;
45467  case cimg::keyENTER : if (selected<0) selected = 0; stopflag = true; break;
45468  case cimg::keyTAB :
45469  case cimg::keyARROWRIGHT :
45470  case cimg::keyARROWDOWN : selected = (selected+1)%buttons._width; break;
45471  case cimg::keyARROWLEFT :
45472  case cimg::keyARROWUP : selected = (selected+buttons._width-1)%buttons._width; break;
45473  }
45474  disp.set_key();
45475  if (selected!=oselected) refresh = true;
45476  }
45477  }
45478  if (!disp) selected = -1;
45479  return selected;
45480 #endif
45481  }
45482 
45484  inline int dialog(const char *const title, const char *const msg,
45485  const char *const button1_label, const char *const button2_label, const char *const button3_label,
45486  const char *const button4_label, const char *const button5_label, const char *const button6_label,
45487  const bool is_centered) {
45488  return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
45489  CImg<unsigned char>::_logo40x38(),is_centered);
45490  }
45491 
45493 
45508  inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
45509  static const CImg<float> empty;
45510  return empty.eval(expression,x,y,z,c);
45511  }
45512 
45513  // End of cimg:: namespace
45514 }
45515 
45516  // End of cimg_library:: namespace
45517 }
45518 
45520 namespace cil = cimg_library_suffixed;
45521 
45522 #ifdef _cimg_redefine_False
45523 #define False 0
45524 #endif
45525 #ifdef _cimg_redefine_True
45526 #define True 1
45527 #endif
45528 #ifdef _cimg_redefine_None
45529 #define None 0
45530 #endif
45531 #ifdef _cimg_redefine_min
45532 #define min(a,b) (((a)<(b))?(a):(b))
45533 #endif
45534 #ifdef _cimg_redefine_max
45535 #define max(a,b) (((a)>(b))?(a):(b))
45536 #endif
45537 #ifdef _cimg_redefine_PI
45538 #define PI 3.141592653589793238462643383
45539 #endif
45540 
45541 #endif
45542 // Local Variables:
45543 // mode: c++
45544 // End: